A quick intro to the intro to R Lesson Series


This ‘Intro to R Lesson Series’ is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This course was developed based on feedback on the needs and interests of the Department of Cell & Systems Biology and the Department of Ecology and Evolutionary Biology.

This lesson is the fourth in a 6-part series. The idea is that at the end of the series, you will be able to import and manipulate your data, make exploratory plots, perform some basic statistical tests, test a regression model, and make some even prettier plots and documents to share your results.


How do we get there? Today we are going to be learning data cleaning and string manipulation; this is really the battleground of coding - getting your data into the format where you can analyse it. We will also be learning r markdown so that we can easily annotate our code and share it with others in reproducible documents. In the next lesson we will learn how to do t-tests and perform regression and modeling in R. And lastly, we will learn to write some functions, which really can save you time and help scale up your analyses.


The structure of the class is a code-along style. It is hands on. The lecture AND code we are going through are available on GitHub for download at https://github.com/eacton/CAGEF, so you can spend the time coding and not taking notes. As we go along, there will be some challenge questions and multiple choice questions on Socrative. At the end of the class if you could please fill out a post-lesson survey (https://www.surveymonkey.com/r/PVHDKDB), it will help me further develop this course and would be greatly appreciated.


Packages Used in This Lesson

The following packages are used in this lesson:

tidyverse (ggplot2, tidyr, dplyr)
(twitteR)*
(httr)*
tidytext
viridis
knitr
kableExtra
wordcloud

*Used to generate the tweet tables used in this lesson. It is not necessary for you to install this - you can work from the tables. If you want to create these files - the code is here - twitter scrape.

Please install and load these packages for the lesson. In this document I will load each package separately, but I will not be reminding you to install the package. Remember: these packages may be from CRAN OR Bioconductor.


Highlighting

grey background - a package, function, code or command
italics - an important term or concept
bold - heading or ‘grammar of graphics’ term
blue text - named or unnamed hyperlink


Objective: At the end of this session you will be able to use regular expressions to ‘clean’ your data. You will also learn R markdown and be able to render your R code into slides, a pdf, html, a word document, or a notebook.


Load libraries

Since we are moving along in the world, we are now going to start loading our libraries at the start of our script. This is a ‘best practice’ and makes it much easier for someone to reproduce your work efficiently by knowing exactly what packages they need to run your code. We will learn how to do this with a function in Lesson 6!

library("tidyverse")
library("tidytext")
library("viridis")
library("knitr")
library("kableExtra")
library("wordcloud")

Data Cleaning or Data Munging or Data Wrangling

Why do we need to do this?

‘Raw’ data is seldom (never) in a useable format. Data in tutorials or demos has already been meticulously filtered, transformed and readied to showcase that specific analysis. How many people have done a tutorial only to find they can’t get their own data in the format to use the tool they have just spend an hour learning about???

Data cleaning requires us to:

Some definitions might take this a bit farther and include normalizing data and removing outliers, but I consider data cleaning as getting data into a format where we can start actively doing ‘the maths or the graphs’ - whether it be statistical calculations, normalization or exploratory plots.

Today we are going to mostly be focusing on the data cleaning of text. This step is crucial to taking control of your dataset and your metadata. I have included the functions I find most useful for these tasks but I encourage you to take a look at the Strings Chapter in R for Data Science for an exhaustive list of functions. We have learned how to transform data into a tidy format in Lesson 2, but the prelude to transforming data is doing the grunt work of data cleaning. So let’s get to it!



Intro to regular expressions

Regular expressions

“A God-awful and powerful language for expressing patterns to match in text or for search-and-replace. Frequently described as ‘write only’, because regular expressions are easier to write than to read/understand. And they are not particularly easy to write.” - Jenny Bryan



So why do regular expressions or ‘regex’ get so much flak if it is so powerful for text matching?

Scary example: how to get an email in different programming languages http://emailregex.com/.

Regex is definitely one of those times when it is important to annotate your code. There are many jokes related to people coming back to their code the next day and having no idea what their code means.

There are sites available to help you make up your regular expressions and validate them against text. These are usually not R specific, but they will get you close and the expression will only need a slight modification for R (like an extra backslash - described below).

Regex testers:

https://regex101.com/
https://regexr.com/

What I would like to get across it that it is okay to google and use resources early on for regex, and that even experts still use these resources.







What does the language look like?

The language is based on meta-characters which have a special meaning rather than their literal meaning. For example, ‘$’ is used to match the end of a string, and this use supercedes its use as a character in a string (ie ‘Joe paid $2.99 for chips.’).

Matching by position

Where is the character in the string?

Quantifiers

How many times will a character appear?

Classes

What kind of character is it?

Operators

Helper actions to match your characters.

Escape characters

Sometimes a meta-character is just a character. Escaping allows you to use a character ‘as is’ rather than its special function. In R, regex gets evaluated as a string before a regular expression, and a backslash is used to escape the string - so you really need 2 backslashes to escape, say, a ‘$’ sign ("\\\$").

Trouble-shooting with escaping meta-characters means adding backslashes until something works.

Joking/Not Joking (xkcd)

Joking/Not Joking (xkcd)

While you can always refer back to this lesson for making your regular expressions, you can also use this regex cheatsheet.


Data Cleaning with Base R (AKA What is Elon Musk up to anyways?)

Let’s take this cacaphony of characters we’ve just learned about and perform some basic data cleaning tasks with an actual messy data set. I have scraped Elon Musk’s latest tweets from Twitter. The code to do this is in the Lesson 4 file twitter_scrape.R if you are curious or want to creep someone on Twitter.

Let’s read in the set of tweets, take a look at the structure of the data.

elon_tweets_df <- read.delim("data/elon_tweets_df.txt", sep = "\t", stringsAsFactors = F)
EOF within quoted string

The warning with EOF (end of file) within quoted string is possibly due to the fact that there are special characters (emojis, arrows, etc.) inside the cells. Let’s take a look at how the file was parsed.

str(elon_tweets_df)

Our end goal is going to be to look at the top 50 words in Elon Musk’s tweets and make a wordcloud. I don’t want urls, hastags, or other tags. I also don’t want punctuation or spaces. I just want to extract the words from tweets. It might be fun to look at the top favorite tweets while we are data cleaning, so let’s use tidyverse functions to keep the text tweets and order them by the favorited counts.

elon_tweets_df <- elon_tweets_df %>% 
  select(text, favoriteCount) %>%
  arrange(desc(favoriteCount))

elon_tweets_df$text[1:5]

First, I want to remove the tags from the beginning of words. I am going to save my regex expression into an object - so we can use them again later.

What this expression says is that I want to find matches for a hastag OR an asperand (‘at’ symbol) followed by at least one word character. grep is a function that allows us to match our pattern (our expression) to a character vector. It is a good idea to do a visual inspection of your result to make sure your matches or substitutions are working the way you expected.

tags <- "#|@\\w+"

grep(pattern = tags, x = elon_tweets_df$text)

We can see that grep returns the index of the match. We have a number of entries that include tags. We also have a number of warnings that we will return to.

If we want to return the tweet itself instead of the index, we can use the argument value = TRUE. In this case, it looks like each tweet matched does have a tag. (You will have a warning here too, I didn’t print it here.)

grep(tags, elon_tweets_df$text, value = TRUE) %>% head()

We can then use gsub to replace that pattern (our tags) with nothing (an empty string).

elon_tweets_df$text <- gsub(pattern = tags, replacement = "", elon_tweets_df$text)

Back to the warnings about strings being ‘invalid in this locale’. Let’s take a look at these strings by subsetting for the indices given.

elon_tweets_df$text[c(10,118, 156, 219, 224)]

From context, it looks like these character strings have emojis in them, which have their own character codes. Why would this give us an error? Tweets are encoded in UTF-16 and converted to UTF-8 when read into R. Things that have character codes get encoded differently. Here is an example of emoji encoding. Since we are going to remove anything with special character codes (ie. an apostrophe or emoji), we are going to use the iconv function to substitute encoded character codes that need converting with nothing (again, an empty character string). This is not something you will have to deal with on a daily basis, but character encoding is something to be aware of, especially when scraping data from the web.

elon_tweets_df$text <- iconv(elon_tweets_df$text, "UTF-8", "ASCII", sub = "")

elon_tweets_df$text[c(10,118, 156, 219, 224)]

Looking back at our problematic strings, you can see that the emojis have been removed as well as quotation marks. Our hastag and asperand would also have been encoded characters had we not already removed them.

Our next step would be to remove urls. This is a bit tricky. We could be looking for http:// or https:// followed by we don’t know what (some combination of letters, numbers and forward slashes).

We can check out which tweets have urls using grep as we did previously to see if we managed to match urls.

We are going to continue our pattern of using gsub to substitute what we don’t want with an empty character string.

url <- "http[s]?://[[:alnum:].\\/]+"

grep(url, elon_tweets_df$text, value = TRUE) %>% head()

elon_tweets_df$text <- gsub(pattern = "http[s]?://[[:alnum:].\\/]+", replacement = "", elon_tweets_df$text)

We can also use grepl to get a logical reponse for whether a tweet has a url or not. That way, if you wanted to grab all of the urls that Elon Musk suggests to visit, you can filter with grepl to select all of the tweets where it is TRUE that a url is present.

grepl(url, elon_tweets_df$text) %>% head()

elon_urls <- elon_tweets_df %>% filter(grepl(url, elon_tweets_df$text))

Lastly, we are going to get rid of trailing spaces, numbers, and punctuation all at the same time. You can find trailing spaces at the very end of our tweet string from removing the urls.

trail <- "[ ]+$|[0-9]*|[[:punct:]]"

grep(trail, elon_tweets_df$text, value = TRUE) %>% head()

We can check to see that we are picking up strings with punctutation, numbers and trailing spaces, and then we can remove them and compare our output.

elon_tweets_df$text <- gsub(pattern = trail, replacement = "", elon_tweets_df$text)

elon_tweets_df$text[1:5]

It looks like everything worked except there are extra spaces from whenever a number was removed. Let’s take all of the places where there are 2 or more spaces created and substitute them with just one space.

space <- "\\s{2,}"

grep(space, elon_tweets_df$text, value = TRUE) %>% head()

Again, we can check to see that we are picking up strings with extra spaces, and then replace those spaces with a single space.

elon_tweets_df$text <- gsub(pattern = space, replacement = " ", elon_tweets_df$text)

elon_tweets_df$text[1:5]

It worked!


Challenge

We also have a leading whitespace where we removed a number. How would we remove that whitespace? Can you think of more than one way to do this?






Onwards!! Let’s break the tweets down into individual words, so we can see what the most common words used are. We can use the base R function strsplit to do this; in this case we want to split our tweets into words using spaces.

strsplit(elon_tweets_df$text, split = " ") %>% head()

Note that the output of this function is some horrible nested list object.

Luckily there is an unlist function which recursively will go through lists to simplify their elements into a vector. Let’s try it and check the structure of our output. We will save this to an object called ‘words’.

unlist(strsplit(elon_tweets_df$text, split = " ")) %>% head(20)

words <- unlist(strsplit(elon_tweets_df$text, split = " "))

Our output is now a long character vector. This will make it much easier to count words.

str(words)

Let’s take a peak at the words.

tail(words)

Great! But… we missed some \n (newline) and \t (tab) characters. These are not punctuation characters.


Challenge

Newline and tab characters are separating 2 words. Split these words apart and get rid of the newline character. Convert all of our character strings to lowercase (I haven’t shown you how to do this, but I believe in your google-fu). Check the first and last 50 words to see if anything else is amiss.





There are still a few problems with words cutoff like ‘solv’, or ‘flamethrower’ and ‘flamethrowers’ being the same word, or ‘north’ and ‘korea’ belonging together for context. If we were serious about this dataset we would need to resolve these issues. We also have some html and twitter-specific tags that we will deal with shortly.

Let’s move ahead and count the number of occurences of each word and order them by frequency. We do this using our dplyr functions (Lesson 2).

data.frame(words) %>% count(factor(words)) %>% arrange(desc(n))

Wow. We have discovered people use prepositions and conjunctions. There are also words unrelated to content but that are html jargon, or things like ‘na’ and ‘false’.

Luckily text mining is an area of data analytics in full force and there is a list of ‘stop words’ that can be used to get rid of words that are unlikely to contain useful information as part of the tidytext package. However, we will have to add to this list.

The data that comes with the package is called stop_words. We can save it as an object and take a look at its structure.

stop_words <- stop_words
str(stop_words)

We can then add rows to this data frame with words our own stop words. Remember that to bind_rows data frames together, the column names have to match. We can make a small data frame and call our lexicon ‘custom’. Note that I have written ‘custom’ once - it will recycle as a character vector of length 1 to the length of the data frame.

add_stop <- data.frame(word = c("na", "false", "href", "rel", "nofollow", "true", "amp", "twitter", "iphonea", "relnofollowtwitter", "relnofollowinstagrama"), 
                       lexicon = "custom", stringsAsFactors = FALSE)

stop_words <- bind_rows(stop_words, add_stop)

To remove these stop words from our list of words from tweets, we perform an anti-join (from Lesson 3).

words <- anti_join(data.frame(words), stop_words, by=c("words" = "word"))

Let’s look at our top words by count now, and save this order.

words %>% count(words) %>% arrange(desc(n))

words <- words %>% count(words) %>% arrange(desc(n))

‘boring’, ‘falcon’, ‘tesla’, ‘rocket’, ‘launch’,‘flamethrower’, ‘cars’, ‘spacex’, ‘tunnels’, and ‘mars’ and ‘ai’ are a bit further down the list. There are a few words that look like they should be added to the ‘stop words’ list (dont, doesnt, didnt, im), but we’ll work with this for now.

We can make a word cloud out of the top 50 words, which will be sized according to their frequency. I am starting with the first word after Elon Musk’s twitter handle. The default color is black, but we can use our viridis package (Lesson 3) to have a pleasing color palette. It is okay if this code gives you a warning that not all words can be fit on the page, this can be changed by adjusting the scale argument.


Data Cleaning with stringr/stringi (AKA What is Trump up to anyways?)

We are going to go through the same data cleaning process with the stringr package using Trump’s tweets. The syntax is a little different, but it is pretty intuitive once you get started. All stringr functions can be found using str_ + Tab. Again, we will start by loading the dataset and looking at the top 5 favorite tweets. We will remove all encoded character codes right away.

trump_tweets_df <- read.delim("data/trump_tweets_df.txt", sep = "\t", stringsAsFactors = FALSE)
trump_tweets_df$text <- iconv(trump_tweets_df$text, "UTF-8", "ASCII", sub = "")

trump_tweets_df <- trump_tweets_df %>% select(text, favoriteCount) %>% arrange(desc(favoriteCount)) 
trump_tweets_df$text[1:5]

The first thing that we did was look for tags. The order of arguments are switched in stringr relative to the base functions. The first argument will be the character string we are searching, and the second argument will be the pattern we are matching. str_extract will return the index of the match, as well as the match. This is similar to grep when value = TRUE. Note that the match is extracted rather than the entire string.

str_extract(string = trump_tweets_df$text, pattern = tags) %>% head(100)

str_detect is similar to grepl returning TRUE or FALSE if a match is or isn’t found, respectively.

str_detect(trump_tweets_df$text, tags)

Let’s remove our urls as before. With the str_replace function we can specify our pattern and replacement, in this case an empty character string. We can see in the result that the urls have been replaced.

str_replace_all(trump_tweets_df$text[1:10], pattern = url, replacement = "")
trump_tweets_df$text <- str_replace_all(trump_tweets_df$text, pattern = url, replacement = "")

Let’s be ambitious and try to remove tags, numbers and punctuation characters and numbers all in one go. str_remove automatically replaces the match with an empty character string. It turns out the @ and # are punctuation characters, so removing them is taken care of using [[:punct:]]. We also want to remove the metacharacter $ (which is not considered punctuation. We aren’t sure what order the numbers and punctuation might come in and square brackets allow ANY characters inside the brackets to be matched. We are not sure if there will be zero, one, or many of our target characters in a tweet, however str_remove_all() will remove every instance of this pattern (otherwise we would use the * outside the brackets to indicate 0 or more times). Looking at the output, we can see that the numbers and punctuation and dollar signs are indeed removed.

clean_all <- "[[0-9][[:punct:]]\\$]"

trump_tweets_df$text <- str_remove_all(trump_tweets_df$text, pattern = clean_all)

trump_tweets_df$text[1:10]

As expected, we still have trailing spaces. Whitespace characters are not visible, but take up space. Newline characters, tabs and spaces are a form of whitespace. stringr has its own function for trimming whitespace, str_trim, which you can use to specify whether you want leading or trailing whitespace trimmed, or both.

trump_tweets_df$text <- str_trim(trump_tweets_df$text, side = "both")

trump_tweets_df$text[1:10]

See how we have a couple extra spaces in the middle of some of our strings? str_squish will take care of that for us, leaving only a single space between words.

trump_tweets_df$text <- str_squish(trump_tweets_df$text)

trump_tweets_df$text[1:10]

All that’s left is to convert all characters to lowercase, and then we can see the top Trump words!

trump_tweets_df$text <- tolower(trump_tweets_df$text)

trump_tweets_df$text[1:10]

To get our tweets into a word list we use str_split, a similar function to strsplit, still splitting by the spaces between words. The argument simplify = FALSE returns a list of character vectors which we then unlist.

str(words)
 chr [1:8766] "crazy" "joe" "biden" "is" "trying" "to" "act" "like" "a" "tough" "guy" "actually" "he" "is" ...

We can now do our anti_join to remove ‘stop words’, and tally our remaining words and order them by descending counts.

words <- anti_join(data.frame(words), stop_words, by=c("words" = "word"))

words %>% count(words) %>% arrange(desc(n)) 

words <- words %>% count(words) %>% arrange(desc(n))

Hmmm… it looks like we have those html tags in a different format. It’s interesting to note these little variations because no matter how much you try to automate your analysis there is always going to be something from your new dataset that didn’t fit with your old dataset. This is why we need these data wrangling skills. Even though some packages may have been created to help us on our way, they can’t possibly cover every case.



















We could go back and get rid of some of characters such as <, however we don’t want to lose sight that these are html tags and not words (the tweet was from an ipad or iphone, the ‘word’ isn’t being mentioned). We will instead add these to our stop words list.

add_stop <- data.frame(word = c("rel=nofollow>twitter", "href=", "iphone<a>", "<a","dont", "$", "href=downloadipad", "ipad<a>" ), 
                       lexicon = "custom", stringsAsFactors = FALSE)


stop_words <- bind_rows(stop_words, add_stop)

We then perform an anti_join with our new list and view the updated version. (words was already sorted and so we do not need to do that again.)

‘president’, ‘people’, ‘fake’, ‘news’, ‘daca’, democrats’, ‘jobs’, ‘obama’, ‘border’, ‘fbi’, ‘collusion’, ‘russia’, ‘wall’, ‘mexico’ and further down is ‘crooked’ and ‘hillary’.

Trump’s wordcloud minus his twitter handle.


Challenge $

Pick one of the other tweet data sets:

Bill Nye, Justin Trudeau, The Daily Show, Katy Perry, Jimmy Fallon, Stephen Colbert.

Clean it. Remove all of the stop words. Were there any other challenges compared to the previous datasets? Did you have to create new stop words or do extra regex? Make a wordcloud of the top 50 words.



Rmarkdown and knitr

Markdown is a plain text formatting syntax. It allows one to easily add headings, lists, links, highlighting, bullets, images, equations, tables and text styling. R has a modified version of markdown (R markdown) where you can embed code chunks into a document. Combined with the knitr package, this allows us to make reproducible documents. The awesomeness of this combination allows us to annotate our code while we work in a format that is immediately presentable. With the click of a button our script (or notebook) can be converted into a word document, a pdf, or a shareable html hyperlink. Other formats such as slides are also possible. This means that you only have to do your work once - you don’t have to have your code, generate images, paste them into powerpoint or word - get asked to change something, rerun the code, get the figure, change your powerpoint… everything is all in one place - you make your change, knit your document and you are done - you don’t have to leave RStudio.

Markdown and knitr can also save us time scrolling through our code - a table of contents can be added, code chunks can be named - both of which allow us to jump around and navigate our script easily. It takes a bit of discipline to get started, but I believe that you will see the benefits pretty quickly. There are even markdown templates for submitting to different journals or writing a thesis.

For this lesson I suggest going to Tools -> Global Options -> R Markdown -> Show output preview in: and change from ‘Window’ to ‘Viewer Pane’, then click ‘Apply’ and ‘OK’. This will allow us to see our new document in the same window (in the Viewer) instead of switching back and forth between our code and the document in a separate window.

R markdown syntax

Let’s start by creating a new R markdown document by going to File -> New File -> R Markdown. R will ask you for the Title of your document, the Author and whether you want to render your markdown as html, pdf, or as a word document. There is also the option to make slides (under Presentation), a Shiny app, or use Templates for package documentation or GitHub (there is a git version of markdown that differs slightly from R markdown). html renders faster than the other formats, so we will stick with that and click OK. R immediately puts a yaml (yet another markup language, yaml ain’t markup language) header which tells you how the file is configured.

---
title: "R markdown Lesson"
author: "EA"
date: "April 11, 2018"
output: html_document
---

As you can see, the date has been added (this is the date of script creation and does not update when the script is rendered), and the output is going to be html. Note also that your document is Untitled. We can go ahead and save that as ‘Rmarkdown_Lesson’ and see that the file is saved as a .Rmd file.

We can see that R has a little demo set up already which we are going to work with. The new element in .Rmd files are these code chunks denoted by a set of 3 backticks, followed by {r}, some code and a closing set of 3 backticks. The keyboard shortcut for generating a code chunk is CTRL + ALT + I.

```{ r name_of_chunk, code_options}

type code here

```

These code chunks have been named ‘setup’, ‘cars’, and ‘pressure’, and at the bottom left the source pane (or if you go to Code -> Jump To... it will pop up) you can navigate between these code chunks. This is a helpful feature as your code gets a bit longer.

You have probably also noticed that in this navigation bar the bolded items correspond to the title of the document and the text with a leading ##. Hashtags (when outside code chunks, ie. in markdown language) denote headers. The number of hashtags denotes the level of the header as well as the size. For example, the title is a first level header, and ‘R Markdown’ and ‘Including Plots’ are second level headers, and will also be smaller than the title. Let’s go ahead and knit our document by clicking the Knit button. Note that you can change from your default output choice (html) to Word or pdf in the dropdown menu.

Let’s look at how markdown is rendered in our html file. We can see that to bold text you can have two asteriks (**) or two underscores (__) on either side of the text.

You can insert a url by typing the url inside of arrow brackets <http://rmarkdown.rstudio.com>. If you want the link to be named ‘rmarkdown’ can format it like this [rmarkdown] (http://rmarkdown.rstudio.com) without the space inbetween the name and the url. Replace the url with the named version and click knit to see the difference in the output.

The other emphasized text in this document has a grey background. This is achieved by flanking the text with `backticks`. The code in the ‘cars’ chunk has a grey background and the evaluated output is in white. This is standard in the R community for the code to be grey and the output to be white and commented. Inline code can be written by using {r 2+2} and it will also have a grey background.

Rmarkdown

To make a bulleted list:

* you need to leave a line before 
* the text and the start of your bullets and a 
* space between the asterik (bullet) and your text.

Rendered

To make a bulleted list:

  • you need to leave a line before
  • the text and the start of your bullets and a
  • space between the asterik (bullet) and your text.

Rmarkdown + Rendered

To make a numbered list:

  1. you need to leave a line before
  2. the text and the start of your numbers and a
  3. space between the numbers and your text.

Rmarkdown

To make a super-cool updatable numbered list:

1. you need to do the above as with numbered lists
1. but all of the numbers are numbered '1.' 
1. you can now add, remove and reorder and your numbers will update.

Rendered

To make a super-cool updatable numbered list:

  1. you need to do the above for numbered lists
  2. but all of the numbers are numbered ‘1.’
  3. you can now add, remove and reorder and your numbers will update.

Rmarkdown

If ever your text
is clumping together
when you do not expect it to,
remember that you need 5 spaces at at the end of a line
to start a new line.

Rendered

If ever your text is clumping together when you do not expect it to, remember that you need 5 spaces at at the end of a line for a new line to start.

Rmarkdown

    A text box can be created by indenting with Tab twice.

Rendered

A text box can be created by indenting with Tab twice.

Rmarkdown

A line across the page is ’***’.

Rendered


Knitr Chunk Options

You might notice that while there are 3 code chunks in this example, there is one line of code visible in the rendered version, and 2 outputs (summary statistics and a plot). Why don’t we see the code used to make the plot? Code chunks have options that can be entered to modify their output. In this case the inclusion of echo = FALSE prevents the code from being included, but the code is still run and so the plot is still produced. Try changing the code chunk option for the plot to eval = FALSE. What happened?

{r pressure, eval = FALSE}

plot(pressure)

eval = FALSE means the code is shown, but not evaluated.

You can specify which lines of code in a chunk get evaluated. For example if you had 5 lines of code, but only wanted to run the first and third, you could use eval = c(1,3). This feature of using a vector of position to specify code is available for other chunk options such as echo.

The first code chunk in this script is setting default options for all code chunks to be used in this script. In this case echo = TRUE was set as a default chunk option. The option include = FALSE for this chunk means that the code will not be included, but the code will still be run. The difference between this command and echo = FALSE is that the output of the code is NOT shown.

Let’s look at what the default chunk options are - this way we will be able to see all of the options available to change.

{r setup, echo = TRUE }

str(knitr::opts_chunk$get())
List of 1
 $ error: logi FALSE

There are a ton of options here. You can guess that some of them have to do with default figure sizes and labels, and there a bunch of options that are not specified (NULL).

As far as setting chunk options at the beginning of a script goes, consider the following:

{r}

library(tidyverse)

If we are creating a document, we may want to show what package we used, but we don’t want all of the package startup messages. With message = FALSE the code will run and be shown but any messages generated will be suppressed.

{r message = FALSE}

library(tidyverse)

I could use include = FALSE, message = FALSE if I wanted the library loaded but didn’t want the code or its message to be seen.

If I wanted to do something silly like add 6 to every summary value (in truth each of these summary values is a character) it would generate an error. A document will not be rendered if it has an error in it. Try to knit the document with this code.

{r error = TRUE}

summary(cars) + 6

Adding the option error = TRUE allows the document to be rendered despite the error. The error message will still be shown.


Challenge

For the code chunk containing plot(pressure): How would you show just the code (not the plot) and not run the code? How would you show just the code and have the output run but not show it? How would you change the background color to something other than gray? You can use help pages, Google, or the knitr documentation found here: https://yihui.name/knitr/options/#chunk_options




Caching

In the ‘Run’ dropdown menu (found in the top right of the source pane), there are various options for running your current chunk - CTRL+SHIFT+ENTER, the next chunk - CTRL+ALT+N, all chunks above - CTRL+ALT+P, all chunks below, and another few options. This allows you to assess the upstream and downstream consequences of a change in your code. knitr also has the option to cache the output of code chunks by setting the option cache = TRUE. A folder will be created that saves the output of your chunk in a data file. knitr accesses the cache and loads the result from the last time the chunk was run without recalculating values. This can be very useful if the code in a particular chunk takes awhile to run and you are assessing changes unrelated to that code, or changes after that code.

For example, if your document isn’t knitting because of an error at line 200 and your time intensive code runs at line 100, you can cache the line 100 chunk and troubleshoot the line 200 code without having to wait for this earlier chunk to run again. The caching caveat is that changing anything in earlier code (at line 50 in this example) that your cached chunk depends on would not be appropriately updated (ie. the code at line 100 would still not change). Therefore it is important to be conscious of what you are caching and where changes are occurring in your script. You should uncache your code chunk for the final rendering to make sure there haven’t been any unforseen changes to your document.

This is a simplified explanation of caching and more details can be found in the knitr manual and its cache demo.

Playing with Caching

Note to David: These caching scenarios will partly be on Socrative. There will be multiple choice answers about the output and students vote on which is correct. This will allow me to track comprehension across the series. Therefore this text will be in the online version of the notes.

Scenario 1

With our current .Rmd file, let’s say the ‘summary’ chunk took awhile to run. Let’s add cache = TRUE to its chunk options. Make sure eval = FALSE has been removed from the options in your ‘plot’ chunk. Knit the document and take a look.

We are now going to change the plot chunk to depend on the cars dataset.

{r cars}

plot(cars$speed, cars$dist)

We can knit the document again, and assume that we saved ourselves the time cost of running the second chunk when we are just updating a plot.

Now let’s put a chunk before our cached chunk called ‘new row’. Add a point to the cars dataset using dplyr’s bind_rows. Note that I haven’t loaded the entire dplyr package, but rather have just made a call to one specific function. Knit the document again.

{r new row}

cars <- dplyr::bind_rows(cars, c(speed = 50, dist = 200))

This point is an outlier, and the change can be seen on the output of our plot. However, our summary also depends on the cars dataset and has not been updated (ie. the maximum distance is still 120 km). If the code of the cached chunk does not change, the chunk is not rerun.

Scenario 2

If I change the cached chunk, say, by adding another outlier data point - what do you think will happen? Add this point and knit again.

{r cars, message = FALSE, cache = TRUE}

cars <- dplyr::bind_rows(cars, c(speed = 100, dist = 300))
summary(cars)

Since the code in the cache changed, its values were recalculated and the max distance in the summary has changed to 300 km. Changes in the the cached chunk are evaluated and passed on to the next code block. The previous cache values are deleted and replaced with the current values. The plot, which depends on the cars dataset, now has a point at dist = 300 km AND dist = 200 km.

Scenario 3

If I change cars (with the outlier point) in the ‘new row’ chunk back to cars (without the outlier point) and knit the document, what do you expect to happen?

{r new row}

cars <- cars

Since the code for our cached chunk doesn’t change its values are loaded from the cache. This includes the cars dataset, and so the plot, downstream of our cache, does not reflect the change to the ‘new row’ chunk.

Scenario 4

What if we instead comment the outlier point in the cached chunk and keep the extra point from the first chunk?

{r new row}

cars <- cars
cars <- dplyr::bind_rows(cars, c(speed = 50, dist = 200))

{r cars, message = FALSE, cache = TRUE}

#cars <- dplyr::bind_rows(cars, c(speed = 100, dist = 300))
summary(cars)

The code in the cache changed and so the summary was recalcuated using the earlier chunk. Both the summary and the plot show the 200 km distance value.

Scenario 5

What do you expect to happen if both outlier points are commented out and we restart the R session and knit the document? What is the max dist value in the summary table? What about the plot - does it contain the outlier?

{r new row}

cars <- cars
#cars <- dplyr::bind_rows(cars, c(speed = 50, dist = 200))

{r cars, message = FALSE, cache = TRUE}

#cars <- dplyr::bind_rows(cars, c(speed = 100, dist = 300))
summary(cars)

Since the code in the cached chunk did not changed, it accesses the summary from the cached data, which has 200 km as the maximum value. However, the plot needs to be created from scratch and uses cars from the first chunk so has a max of 120 km.

Scenario 6

What if we remove cache=TRUE from our code chunk?

{r cars, message = FALSE}

#cars <- dplyr::bind_rows(cars, c(speed = 100, dist = 300))
summary(cars)

The cache is no longer accessed. The summary table and the plot reflect the original cars dataset with a max of 120 km.

Scenario 7

What if we add cache=TRUE to our code chunk again?

{r cars, message = FALSE, cache = TRUE}

#cars <- dplyr::bind_rows(cars, c(speed = 100, dist = 300))
summary(cars)

The cache files are kept on the computer. The summary table reflects the previously cached value of 200 km, the plot is calculated from the first chunk and has a max of 120 km. The cache will stay this value until it is updated. To start with a fresh cache in the same directory you need to delete your cached files.

Tables

You can make table in markdown, but it is kind of annoying compared to using the kable package to make tables in knitr. Making tables in markdown involves using a series of pipes (|) to make columns and hypens (-) to make column headers. Here is an example of a markdown table. Here is an example of how to format markdown tables: https://help.github.com/articles/organizing-information-with-tables/. Go nuts.

  |Summary      | Values|
  |-------------|-------|
  | correlation |   0.8068949|
  | mean km/h   |    15.4|
  | mean km     |    42.98|
 
Summary Values
correlation 0.8068949
mean km/h 15.4
mean km 42.98

Rounding Values

The value for ‘correlation’ in this table was really the output of ‘r cor(cars$speed, cars$dist)’ which gives the value of 0.8068949. To round values is as easy as selecting the number of significant digits using the round function: ‘r round(cor(cars$speed, cars$dist), 2)’ would then give 0.81 two significant digits. The kable tables were are going to work with have a digits argument which gets passed to the round function (usage: digits = 2).

Kable tables in knitr

In this lesson we are going to focus on nice looking kable tables, which are easily customizable through the kableExtra package. The summary stats from cars is a table. However, it doesn’t look very good. Here is a reminder of the default output.

summary(cars)

This summary is an odd table object. If we turn it into a data frame it will be easier to work with.

dat <- data.frame(speed = summary(cars)[,1], distance = summary(cars)[,2])

Here, a simple call to kable creates a table styled similar to the above markdown.

kable(dat)

A variety of styles are offered with simple syntax. Here we have striped rows which highlight when you hover over them, the table width iis the length of the longest text and not across the whole page, and the table is left-aligned.

kable(dat, "html")  %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE, position = "left")

You can also have the table move to the left or right side of your document so that text or a figure could be included beside it. In this case, having the table float right allows for text or images to be formatted on the left side of the page. For example, we could change the figure size as well and have a figure and table side-by-side in our document.


kable(dat, "html", escape = F)  %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE, position = "float_right") 

In this case, I shrank the plot using out.width and out.height so that it would fit beside our table.

{r pressure, echo = FALSE, fig.width=6, fig.height = 5, out.width='50%', out.height='50%'}

par(mar = c(4,4,1,0)) #adjusting figure margins
plot(cars$speed, cars$dist)



Customize with highlighting and borders.

For this table black lines have been specified as column borders. A row was specified to be highlighted by a yellow background as well as to have the text emphasized in bold. escape = FALSE escapes specical characters. In this case it interferes with our column titles.

kable(dat, "html") %>%
       kable_styling("striped", full_width = FALSE) %>%
       column_spec(1:2, border_right = TRUE, border_left = TRUE) %>%
       row_spec(3, bold = T, color = "black", background = "yellow") 
Add footnotes.

Footnotes can be added to a table using symbols or alphabet markers for flags.

This is a good time to learn more useful data cleaning functions paste and paste0. These are made to join or ‘paste’ string characters together. In this case we want to take a character string (the title of each column of our data frame) and add a footnote symbol to it to denote units. Can you tell what the difference is between the 2 functions by the output?

colnames(dat)[1] <- paste("car_", colnames(dat)[1], footnote_marker_symbol(1))
colnames(dat)[2] <- paste0("car_", colnames(dat)[2], footnote_marker_alphabet(1))

Escape has been changed to FALSE so that the html encoding of our superscript is not escaped. The legend for the footnote symbol or character below the table is also added in out kable call.

kable(dat, "html", escape = FALSE) %>%
  kable_styling("striped", full_width = F) %>%
  column_spec(1:2, border_right = TRUE, border_left = TRUE) %>%
  row_spec(3, bold = T, color = "black", background = "yellow") %>%
  footnote(symbol = "kilometers per hour", alphabet = "kilometers;") 

Adding Images to your Document

To add pictures to your document:

! [#caption (optional)]   (#directory/file)     {#size (optional)}     

![knitr - get it?](img/kitten-with-string.pjg){width=400px}

knitr - get it?

knitr - get it?

Minimum syntax to add an image (no caption, default image size):
![](img/kitten-with-string.jpg)

Table of contents

This is the yaml header including the table of contents (toc) for the lessson. It is as simple as writing toc = TRUE under the output for the document type you are using and then specifying what level of headers (remember our hashtags) you would like to include in the toc. I am keeping 1st, 2nd, and 3rd level headers in this example. If I had a 4th level header, it would still be larger than my text, but it will not show up in my table of contents. The toc creates a hyperlink to each section for the user to navigate the document. CTRL+SHIFT+O opens the document outline which allows navigation to these sections while coding.

---
title: "Lesson 4 - Of Data Cleaning and Documentation - Conquer Regular Expressions, Use R markdown and knitr to make PDFs, and Challenge yourself with a 'Real' Dataset"
output: 
  html_document:
          keep_md: yes
          toc: TRUE
          toc_depth: 3
  html_notebook:
          toc: TRUE
          toc_depth: 3
---

You may have noticed the blue button that kind of looks like an eyeball in the top right corner of the Viewer Pane as well as the Source Pane with a dropdown that says ‘Publish’. If you are super-proud of your work, you can post your rendered document for free, for the world to see at Rpubs. It can be interesting to see what other people in the R community have been working on as well.

Slides

Slideshows can also be made fairly simply in R markdown. Go to File -> New File -> R Presentation and create an .RPres file. Slides are separated by a series of equals lines (===) and the title of the slide is just above these lines.

  First Slide
  ========================================================

  For more details on authoring R presentations please visit <https://support.rstudio.com/hc/en-us/articles/200486468>.

  - Bullet 1
  - Bullet 2
  - Bullet 3

Slide With Code
========================================================
```r
summary(cars)
```
Slide With Plot
========================================================
```r
plot(cars)
```

If you click on ‘Preview’ in the Source Pane, a Presentation Tab will open in the Environment Pane with a a slideshow that you can toggle through. In that Pane under ‘More’ you can also ‘View in Browser’ or ‘Save As Webpage’, which is the common way these slides get presented.

I really just wanted to show you that these slides exist. Depending on what you are presenting, this could be a quick alternative to Powerpoint if you are need to present some code. Again, these are customizable https://rmarkdown.rstudio.com/ioslides_presentation_format.html.

If you are interested in a separate tutorial on making and customizing ioslides or the fancier Slidify slides, please leave a comment in the Lesson 4 survey (https://www.surveymonkey.com/r/PVHDKDB).


A Real Messy Dataset

I looked for a messy dataset for data cleaning and found it in a blog titled:
“Biologists: this is why bioinformaticians hate you…”

The main and common issue with this dataset is that when data entry was done there was no structured vocabulary; people could type whatever they wanted into free text answer boxes instead of using dropdown menus with limited options, giving an error if something is formatted incorrectly, or stipulating some rules (ie. must be all lowercase, uppercase, no numbers, spacing, etc).

I must admit I have been guilty of messing with people who have made databases without rules. For example, giving an emergency contact, there was a line to input ‘Relationship’, which could easily have been a dropdown menu: ‘parent, partner, friend, other’. Instead I was allowed to write in a free text box ‘lifelong kindred spirit, soulmate and doggy-daddy’. I don’t think anyone here was trying to be a nuisance, this messy data is just a consequence of poor data collection.

Challenge:

This is Wellcome Trust APC dataset on the costs of open access publishing by providing article processing charge (APC) data.

https://figshare.com/articles/Wellcome_Trust_APC_spend_2012_13_data_file/963054

What I want to know is:

  1. List 3 problems with this dataset that require data cleaning.
  2. What is the mean cost of publishing for the top 3 most popular publishers?
  3. What is the number of publications by PLOS One in dataset?
  4. Convert sterling to CAD. What is the median cost of publishing with Elsevier in CAD?
  5. Annotate your data cleaning efforts and answers to these questions in an .Rmd file. Knit your final answers to pdf.

The route I suggest to take in answering these question is:

There is a README file to go with this spreadsheet if you have questions about the data fields.


The blogger’s opinion of cleaning this dataset:

‘I now have no hair left; I’ve torn it all out. My teeth are just stumps from excessive gnashing. My faith in humanity has been destroyed!’

Don’t get to this point. The dataset doesn’t need to be perfect. No datasets are 100% clean. Just do what you gotta do to answer these questions.

We can talk about how this went at the beginnning of next week’s lesson.


Resources:
http://stat545.com/block022_regular-expression.html
http://stat545.com/block027_regular-expressions.html
http://stat545.com/block028_character-data.html
http://r4ds.had.co.nz/strings.html http://www.gastonsanchez.com/Handling_and_Processing_Strings_in_R.pdf
http://varianceexplained.org/r/trump-tweets/
http://www.opiniomics.org/biologists-this-is-why-bioinformaticians-hate-you/
https://figshare.com/articles/Wellcome_Trust_APC_spend_2012_13_data_file/963054
http://www.datacommunitydc.org/blog/2013/08/fantastic-presentations-from-r-using-slidify-and-rcharts/
https://github.com/rdpeng/cachesweave/blob/master/inst/doc/cacheSweave.Rnw
http://emailregex.com/
https://regex101.com/
https://regexr.com/
https://www.regular-expressions.info/backref.html
https://www.rstudio.com/wp-content/uploads/2016/09/RegExCheatsheet.pdf
https://raw.githubusercontent.com/today-is-a-good-day/Emoticons/master/emDict.csv
http://rmarkdown.rstudio.com
https://yihui.name/knitr/options/#chunk_options
https://www.cs.bham.ac.uk/~axj/pub/teaching/2016-7/stats/knitr-manual.pdf
https://yihui.name/knitr/demo/cache/
https://help.github.com/articles/organizing-information-with-tables/
https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html#getting_started
https://rmarkdown.rstudio.com/ioslides_presentation_format.html
https://rpubs.com/
https://www.jvcasillas.com/slidify_tutorial
http://r4ds.had.co.nz/strings.html

Post-Lesson Assessment


Your feedback is essential to help the next cohort of trainees. Please take a minute to complete the following short survey: https://www.surveymonkey.com/r/PVHDKDB




Thanks for coming!!!

LS0tCnRpdGxlOiAiTGVzc29uIDQgLSBPZiBEYXRhIENsZWFuaW5nIGFuZCBEb2N1bWVudGF0aW9uIC0gQ29ucXVlciBSZWd1bGFyIEV4cHJlc3Npb25zLCBVc2UgUiBtYXJrZG93biBhbmQga25pdHIgdG8gbWFrZSBQREZzLCBhbmQgQ2hhbGxlbmdlIHlvdXJzZWxmIHdpdGggYSAnUmVhbCcgRGF0YXNldCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgICAgICAga2VlcF9tZDogeWVzCiAgICAgICAgICB0b2M6IFRSVUUKICAgICAgICAgIHRvY19kZXB0aDogMwogIGh0bWxfbm90ZWJvb2s6CiAgICAgICAgICB0b2M6IFRSVUUKICAgICAgICAgIHRvY19kZXB0aDogMwotLS0KKioqCiFbXShpbWcvYmlnLWRhdGEtYm9yYXQucG5nKXt3aWR0aD00MDBweH0gCgo8L2JyPgoKIyNBIHF1aWNrIGludHJvIHRvIHRoZSBpbnRybyB0byBSIExlc3NvbiBTZXJpZXMKCjwvYnI+CgpUaGlzICdJbnRybyB0byBSIExlc3NvbiBTZXJpZXMnIGlzIGJyb3VnaHQgdG8geW91IGJ5IHRoZSBDZW50cmUgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uICYgRnVuY3Rpb24ncyAoQ0FHRUYpIGJpb2luZm9ybWF0aWNzIHRyYWluaW5nIGluaXRpYXRpdmUuIFRoaXMgY291cnNlIHdhcyBkZXZlbG9wZWQgYmFzZWQgb24gZmVlZGJhY2sgb24gdGhlIG5lZWRzIGFuZCBpbnRlcmVzdHMgb2YgdGhlIERlcGFydG1lbnQgb2YgQ2VsbCAmIFN5c3RlbXMgQmlvbG9neSBhbmQgdGhlIERlcGFydG1lbnQgb2YgRWNvbG9neSBhbmQgRXZvbHV0aW9uYXJ5IEJpb2xvZ3kuIAoKCgpUaGlzIGxlc3NvbiBpcyB0aGUgZm91cnRoIGluIGEgNi1wYXJ0IHNlcmllcy4gVGhlIGlkZWEgaXMgdGhhdCBhdCB0aGUgZW5kIG9mIHRoZSBzZXJpZXMsIHlvdSB3aWxsIGJlIGFibGUgdG8gaW1wb3J0IGFuZCBtYW5pcHVsYXRlIHlvdXIgZGF0YSwgbWFrZSBleHBsb3JhdG9yeSBwbG90cywgcGVyZm9ybSBzb21lIGJhc2ljIHN0YXRpc3RpY2FsIHRlc3RzLCB0ZXN0IGEgcmVncmVzc2lvbiBtb2RlbCwgYW5kIG1ha2Ugc29tZSBldmVuIHByZXR0aWVyIHBsb3RzIGFuZCBkb2N1bWVudHMgdG8gc2hhcmUgeW91ciByZXN1bHRzLiAKCgohW10oaW1nL2RhdGEtc2NpZW5jZS1leHBsb3JlLnBuZykKCjwvYnI+CgpIb3cgZG8gd2UgZ2V0IHRoZXJlPyBUb2RheSB3ZSBhcmUgZ29pbmcgdG8gYmUgbGVhcm5pbmcgZGF0YSBjbGVhbmluZyBhbmQgc3RyaW5nIG1hbmlwdWxhdGlvbjsgdGhpcyBpcyByZWFsbHkgdGhlIGJhdHRsZWdyb3VuZCBvZiBjb2RpbmcgLSBnZXR0aW5nIHlvdXIgZGF0YSBpbnRvIHRoZSBmb3JtYXQgd2hlcmUgeW91IGNhbiBhbmFseXNlIGl0LiBXZSB3aWxsIGFsc28gYmUgbGVhcm5pbmcgciBtYXJrZG93biBzbyB0aGF0IHdlIGNhbiBlYXNpbHkgYW5ub3RhdGUgb3VyIGNvZGUgYW5kIHNoYXJlIGl0IHdpdGggb3RoZXJzIGluIHJlcHJvZHVjaWJsZSBkb2N1bWVudHMuIEluIHRoZSBuZXh0IGxlc3NvbiB3ZSB3aWxsIGxlYXJuIGhvdyB0byBkbyB0LXRlc3RzIGFuZCBwZXJmb3JtIHJlZ3Jlc3Npb24gYW5kIG1vZGVsaW5nIGluIFIuIEFuZCBsYXN0bHksIHdlIHdpbGwgbGVhcm4gdG8gd3JpdGUgc29tZSBmdW5jdGlvbnMsIHdoaWNoIHJlYWxseSBjYW4gc2F2ZSB5b3UgdGltZSBhbmQgaGVscCBzY2FsZSB1cCB5b3VyIGFuYWx5c2VzLgoKCiFbXShpbWcvc3BvdGlmeS1ob3d0b2J1aWxkbXZwLmdpZikKCjwvYnI+CgpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhIGNvZGUtYWxvbmcgc3R5bGUuIEl0IGlzIGhhbmRzIG9uLiBUaGUgbGVjdHVyZSBBTkQgY29kZSB3ZSBhcmUgZ29pbmcgdGhyb3VnaCBhcmUgYXZhaWxhYmxlIG9uIEdpdEh1YiBmb3IgZG93bmxvYWQgYXQgaHR0cHM6Ly9naXRodWIuY29tL2VhY3Rvbi9DQUdFRiwgc28geW91IGNhbiBzcGVuZCB0aGUgdGltZSBjb2RpbmcgYW5kIG5vdCB0YWtpbmcgbm90ZXMuIEFzIHdlIGdvIGFsb25nLCB0aGVyZSB3aWxsIGJlIHNvbWUgY2hhbGxlbmdlIHF1ZXN0aW9ucyBhbmQgbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9ucyBvbiBTb2NyYXRpdmUuIEF0IHRoZSBlbmQgb2YgdGhlIGNsYXNzIGlmIHlvdSBjb3VsZCBwbGVhc2UgZmlsbCBvdXQgYSBwb3N0LWxlc3NvbiBzdXJ2ZXkgKGh0dHBzOi8vd3d3LnN1cnZleW1vbmtleS5jb20vci9QVkhES0RCKSwgaXQgd2lsbCBoZWxwIG1lIGZ1cnRoZXIgZGV2ZWxvcCB0aGlzIGNvdXJzZSBhbmQgd291bGQgYmUgZ3JlYXRseSBhcHByZWNpYXRlZC4gCgoqKioKCiMjIyNQYWNrYWdlcyBVc2VkIGluIFRoaXMgTGVzc29uCgpUaGUgZm9sbG93aW5nIHBhY2thZ2VzIGFyZSB1c2VkIGluIHRoaXMgbGVzc29uOgoKYHRpZHl2ZXJzZWAgKGBnZ3Bsb3QyYCwgYHRpZHlyYCwgYGRwbHlyYCkgICAgIAooYHR3aXR0ZVJgKVwqICAgICAKKGBodHRyYClcKiAgICAgCmB0aWR5dGV4dGAgICAgIApgdmlyaWRpc2AgICAgIApga25pdHJgICAgICAKYGthYmxlRXh0cmFgICAgICAKYHdvcmRjbG91ZGAgICAgIAoKKlVzZWQgdG8gZ2VuZXJhdGUgdGhlIHR3ZWV0IHRhYmxlcyB1c2VkIGluIHRoaXMgbGVzc29uLiBJdCBpcyBub3QgbmVjZXNzYXJ5IGZvciB5b3UgdG8gaW5zdGFsbCB0aGlzIC0geW91IGNhbiB3b3JrIGZyb20gdGhlIHRhYmxlcy4gSWYgeW91IHdhbnQgdG8gY3JlYXRlIHRoZXNlIGZpbGVzIC0gdGhlIGNvZGUgaXMgaGVyZSAgLSBbdHdpdHRlciBzY3JhcGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9lYWN0b24vQ0FHRUYvYmxvYi9tYXN0ZXIvTGVzc29uXzQvdHdpdHRlcl9zY3JhcGUuUikuICAgIAoKUGxlYXNlIGluc3RhbGwgYW5kIGxvYWQgdGhlc2UgcGFja2FnZXMgZm9yIHRoZSBsZXNzb24uIEluIHRoaXMgZG9jdW1lbnQgSSB3aWxsIGxvYWQgZWFjaCBwYWNrYWdlIHNlcGFyYXRlbHksIGJ1dCBJIHdpbGwgbm90IGJlIHJlbWluZGluZyB5b3UgdG8gaW5zdGFsbCB0aGUgcGFja2FnZS4gUmVtZW1iZXI6IHRoZXNlIHBhY2thZ2VzIG1heSBiZSBmcm9tIENSQU4gT1IgQmlvY29uZHVjdG9yLiAKCgoqKioKIyMjI0hpZ2hsaWdodGluZwoKYGdyZXkgYmFja2dyb3VuZGAgLSBhIHBhY2thZ2UsIGZ1bmN0aW9uLCBjb2RlIG9yIGNvbW1hbmQgICAgICAKKml0YWxpY3MqIC0gYW4gaW1wb3J0YW50IHRlcm0gb3IgY29uY2VwdCAgICAgCioqYm9sZCoqIC0gaGVhZGluZyBvciAnZ3JhbW1hciBvZiBncmFwaGljcycgdGVybSAgICAgIAo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+Ymx1ZSB0ZXh0PC9zcGFuPiAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rICAgICAKCioqKgpfX09iamVjdGl2ZTpfXyBBdCB0aGUgZW5kIG9mIHRoaXMgc2Vzc2lvbiB5b3Ugd2lsbCBiZSBhYmxlIHRvIHVzZSByZWd1bGFyIGV4cHJlc3Npb25zIHRvICdjbGVhbicgeW91ciBkYXRhLiBZb3Ugd2lsbCBhbHNvIGxlYXJuIFIgbWFya2Rvd24gYW5kIGJlIGFibGUgdG8gcmVuZGVyIHlvdXIgUiBjb2RlIGludG8gc2xpZGVzLCBhIHBkZiwgaHRtbCwgYSB3b3JkIGRvY3VtZW50LCBvciBhIG5vdGVib29rLgoKKioqCgojIyMjTG9hZCBsaWJyYXJpZXMKClNpbmNlIHdlIGFyZSBtb3ZpbmcgYWxvbmcgaW4gdGhlIHdvcmxkLCB3ZSBhcmUgbm93IGdvaW5nIHRvIHN0YXJ0IGxvYWRpbmcgb3VyIGxpYnJhcmllcyBhdCB0aGUgc3RhcnQgb2Ygb3VyIHNjcmlwdC4gVGhpcyBpcyBhICdiZXN0IHByYWN0aWNlJyBhbmQgbWFrZXMgaXQgbXVjaCBlYXNpZXIgZm9yIHNvbWVvbmUgdG8gcmVwcm9kdWNlIHlvdXIgd29yayBlZmZpY2llbnRseSBieSBrbm93aW5nIGV4YWN0bHkgd2hhdCBwYWNrYWdlcyB0aGV5IG5lZWQgdG8gcnVuIHlvdXIgY29kZS4gV2Ugd2lsbCBsZWFybiBob3cgdG8gZG8gdGhpcyB3aXRoIGEgZnVuY3Rpb24gaW4gTGVzc29uIDYhCgpgYGB7ciBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoInRpZHl0ZXh0IikKbGlicmFyeSgidmlyaWRpcyIpCmxpYnJhcnkoImtuaXRyIikKbGlicmFyeSgia2FibGVFeHRyYSIpCmxpYnJhcnkoIndvcmRjbG91ZCIpCgpgYGAKCioqKgoKIyNEYXRhIENsZWFuaW5nIG9yIERhdGEgTXVuZ2luZyBvciBEYXRhIFdyYW5nbGluZwoKV2h5IGRvIHdlIG5lZWQgdG8gZG8gdGhpcz8KCidSYXcnIGRhdGEgaXMgc2VsZG9tIChuZXZlcikgaW4gYSB1c2VhYmxlIGZvcm1hdC4gRGF0YSBpbiB0dXRvcmlhbHMgb3IgZGVtb3MgaGFzIGFscmVhZHkgYmVlbiBtZXRpY3Vsb3VzbHkgZmlsdGVyZWQsIHRyYW5zZm9ybWVkIGFuZCByZWFkaWVkIHRvIHNob3djYXNlIHRoYXQgc3BlY2lmaWMgYW5hbHlzaXMuIEhvdyBtYW55IHBlb3BsZSBoYXZlIGRvbmUgYSB0dXRvcmlhbCBvbmx5IHRvIGZpbmQgdGhleSBjYW4ndCBnZXQgdGhlaXIgb3duIGRhdGEgaW4gdGhlIGZvcm1hdCB0byB1c2UgdGhlIHRvb2wgdGhleSBoYXZlIGp1c3Qgc3BlbmQgYW4gaG91ciBsZWFybmluZyBhYm91dD8/PwoKRGF0YSBjbGVhbmluZyByZXF1aXJlcyB1cyB0bzoKCi0gZ2V0IHJpZCBvZiBpbmNvbnNpc3RlbmNpZXMgaW4gb3VyIGRhdGEuIAotIGhhdmUgbGFiZWxzIHRoYXQgbWFrZSBzZW5zZS4gCi0gY2hlY2sgZm9yIGludmFsaWQgY2hhcmFjdGVyL251bWVyaWMgdmFsdWVzLgotIGNoZWNrIGZvciBpbmNvbXBsZXRlIGRhdGEuCi0gcmVtb3ZlIGRhdGEgd2UgZG8gbm90IG5lZWQuCi0gZ2V0IG91ciBkYXRhIGluIGEgcHJvcGVyIGZvcm1hdCB0byBiZSBhbmFseXplZCBieSB0aGUgdG9vbHMgd2UgYXJlIHVzaW5nLiAKLSBmbGFnL3JlbW92ZSBkYXRhIHRoYXQgZG9lcyBub3QgbWFrZSBzZW5zZS4KClNvbWUgZGVmaW5pdGlvbnMgbWlnaHQgdGFrZSB0aGlzIGEgYml0IGZhcnRoZXIgYW5kIGluY2x1ZGUgbm9ybWFsaXppbmcgZGF0YSBhbmQgcmVtb3Zpbmcgb3V0bGllcnMsIGJ1dCBJIGNvbnNpZGVyIGRhdGEgY2xlYW5pbmcgYXMgZ2V0dGluZyBkYXRhIGludG8gYSBmb3JtYXQgd2hlcmUgd2UgY2FuIHN0YXJ0IGFjdGl2ZWx5IGRvaW5nICd0aGUgbWF0aHMgb3IgdGhlIGdyYXBocycgLSB3aGV0aGVyIGl0IGJlIHN0YXRpc3RpY2FsIGNhbGN1bGF0aW9ucywgbm9ybWFsaXphdGlvbiBvciBleHBsb3JhdG9yeSBwbG90cy4gCgpUb2RheSB3ZSBhcmUgZ29pbmcgdG8gbW9zdGx5IGJlIGZvY3VzaW5nIG9uIHRoZSAqKmRhdGEgY2xlYW5pbmcgb2YgdGV4dCoqLiBUaGlzIHN0ZXAgaXMgY3J1Y2lhbCB0byB0YWtpbmcgY29udHJvbCBvZiB5b3VyIGRhdGFzZXQgYW5kIHlvdXIgbWV0YWRhdGEuIEkgaGF2ZSBpbmNsdWRlZCB0aGUgZnVuY3Rpb25zIEkgZmluZCBtb3N0IHVzZWZ1bCBmb3IgdGhlc2UgdGFza3MgYnV0IEkgZW5jb3VyYWdlIHlvdSB0byB0YWtlIGEgbG9vayBhdCB0aGUgW1N0cmluZ3MgQ2hhcHRlcl0oaHR0cDovL3I0ZHMuaGFkLmNvLm56L3N0cmluZ3MuaHRtbCkgaW4gKlIgZm9yIERhdGEgU2NpZW5jZSogZm9yIGFuIGV4aGF1c3RpdmUgbGlzdCBvZiBmdW5jdGlvbnMuIFdlIGhhdmUgbGVhcm5lZCBob3cgdG8gdHJhbnNmb3JtIGRhdGEgaW50byBhIHRpZHkgZm9ybWF0IGluIExlc3NvbiAyLCBidXQgdGhlIHByZWx1ZGUgdG8gdHJhbnNmb3JtaW5nIGRhdGEgaXMgZG9pbmcgdGhlIGdydW50IHdvcmsgb2YgZGF0YSBjbGVhbmluZy4gU28gbGV0J3MgZ2V0IHRvIGl0IQoKPGRpdiBzdHlsZT0iZmxvYXQ6Y2VudGVyO21hcmdpbjogMTBweCAwIDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nL2NsZWFuaW5nLmdpZil7d2lkdGg9MzAwcHh9CjwvZGl2PgoKPC9icj4KCjwvYnI+CgoKIyNJbnRybyB0byByZWd1bGFyIGV4cHJlc3Npb25zCgoKKipSZWd1bGFyIGV4cHJlc3Npb25zKioKCiJBIEdvZC1hd2Z1bCBhbmQgcG93ZXJmdWwgbGFuZ3VhZ2UgZm9yIGV4cHJlc3NpbmcgcGF0dGVybnMgdG8gbWF0Y2ggaW4gdGV4dCBvciBmb3Igc2VhcmNoLWFuZC1yZXBsYWNlLiBGcmVxdWVudGx5IGRlc2NyaWJlZCBhcyAnd3JpdGUgb25seScsIGJlY2F1c2UgcmVndWxhciBleHByZXNzaW9ucyBhcmUgZWFzaWVyIHRvIHdyaXRlIHRoYW4gdG8gcmVhZC91bmRlcnN0YW5kLiBBbmQgdGhleSBhcmUgbm90IHBhcnRpY3VsYXJseSBlYXN5IHRvIHdyaXRlLiIgIC0gSmVubnkgQnJ5YW4KCjwvYnI+CgohW10oaW1nL3hrY2QtMTE3MS1wZXJsX3Byb2JsZW1zLnBuZykKCjwvYnI+CgpTbyB3aHkgZG8gcmVndWxhciBleHByZXNzaW9ucyBvciAncmVnZXgnIGdldCBzbyBtdWNoIGZsYWsgaWYgaXQgaXMgc28gcG93ZXJmdWwgZm9yIHRleHQgbWF0Y2hpbmc/CgpTY2FyeSBleGFtcGxlOiBob3cgdG8gZ2V0IGFuIGVtYWlsIGluIGRpZmZlcmVudCBwcm9ncmFtbWluZyBsYW5ndWFnZXMgPGh0dHA6Ly9lbWFpbHJlZ2V4LmNvbS8+LiAKClJlZ2V4IGlzIGRlZmluaXRlbHkgb25lIG9mIHRob3NlIHRpbWVzIHdoZW4gaXQgaXMgaW1wb3J0YW50IHRvIGFubm90YXRlIHlvdXIgY29kZS4gVGhlcmUgYXJlIG1hbnkgam9rZXMgcmVsYXRlZCB0byBwZW9wbGUgY29taW5nIGJhY2sgdG8gdGhlaXIgY29kZSB0aGUgbmV4dCBkYXkgYW5kIGhhdmluZyBubyBpZGVhIHdoYXQgdGhlaXIgY29kZSBtZWFucy4KCjxkaXYgc3R5bGU9ImxlZnQ7bWFyZ2luOjAgMjBweCAyMHB4IDAiIG1hcmtkb3duPSIxIj4KIVtdKGltZy95ZXN0ZXJkYXlzLXJlZ2V4LnBuZyl7d2lkdGg9NDAwcHh9IAo8L2Rpdj4KClRoZXJlIGFyZSBzaXRlcyBhdmFpbGFibGUgdG8gaGVscCB5b3UgbWFrZSB1cCB5b3VyIHJlZ3VsYXIgZXhwcmVzc2lvbnMgYW5kIHZhbGlkYXRlIHRoZW0gYWdhaW5zdCB0ZXh0LiBUaGVzZSBhcmUgdXN1YWxseSBub3QgUiBzcGVjaWZpYywgYnV0IHRoZXkgd2lsbCBnZXQgeW91IGNsb3NlIGFuZCB0aGUgZXhwcmVzc2lvbiB3aWxsIG9ubHkgbmVlZCBhIHNsaWdodCBtb2RpZmljYXRpb24gZm9yIFIgKGxpa2UgYW4gZXh0cmEgYmFja3NsYXNoIC0gZGVzY3JpYmVkIGJlbG93KS4KClJlZ2V4IHRlc3RlcnM6Cgo8aHR0cHM6Ly9yZWdleDEwMS5jb20vPiAgICAgCjxodHRwczovL3JlZ2V4ci5jb20vPgoKV2hhdCBJIHdvdWxkIGxpa2UgdG8gZ2V0IGFjcm9zcyBpdCB0aGF0IGl0IGlzIG9rYXkgdG8gZ29vZ2xlIGFuZCB1c2UgcmVzb3VyY2VzIGVhcmx5IG9uIGZvciByZWdleCwgYW5kIHRoYXQgZXZlbiBleHBlcnRzIHN0aWxsIHVzZSB0aGVzZSByZXNvdXJjZXMuICAKCgo8L2JyPgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDttYXJnaW46MCAxMHB4IDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nLzgwMTcwYzExOTk2YmQ1OGU0MjJkYmI2NjMxYjczYzRiLmpwZyl7d2lkdGg9MzUwcHh9IAo8L2Rpdj4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvcmVnZXhieXRyaWFsYW5kZXJyb3ItYmlnLXNtYWxsZXIucG5nKXt3aWR0aD0zNTBweH0gCjwvZGl2PgoKPC9icj4KCjwvYnI+Cgo8L2JyPgoKPC9icj4KPC9icj4KCl9fV2hhdCBkb2VzIHRoZSBsYW5ndWFnZSBsb29rIGxpa2U/X18gCgpUaGUgbGFuZ3VhZ2UgaXMgYmFzZWQgb24gX21ldGEtY2hhcmFjdGVyc18gd2hpY2ggaGF2ZSBhIHNwZWNpYWwgbWVhbmluZyByYXRoZXIgdGhhbiB0aGVpciBsaXRlcmFsIG1lYW5pbmcuIEZvciBleGFtcGxlLCAnJCcgaXMgdXNlZCB0byBtYXRjaCB0aGUgZW5kIG9mIGEgc3RyaW5nLCBhbmQgdGhpcyB1c2Ugc3VwZXJjZWRlcyBpdHMgdXNlIGFzIGEgY2hhcmFjdGVyIGluIGEgc3RyaW5nIChpZSAnSm9lIHBhaWQgXCQyLjk5IGZvciBjaGlwcy4nKS4gCgoKIyMjTWF0Y2hpbmcgYnkgcG9zaXRpb24KCldoZXJlIGlzIHRoZSBjaGFyYWN0ZXIgaW4gdGhlIHN0cmluZz8KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZXZhbCA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRX0KCnRleHRfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBFeHByZXNzaW9uID0gYygiXiIsICIkIiwgIlxcXFxiIiwgIlxcXFxCIiksCiAgTWVhbmluZyA9IGMoInN0YXJ0IG9mIHN0cmluZyIsICJlbmQgb2Ygc3RyaW5nIiwgImVtcHR5IHN0cmluZyBhdCBlaXRoZXIgZWRnZSBvZiBhIHdvcmQiLCAiZW1wdHkgc3RyaW5nIHRoYXQgaXMgTk9UIGF0IHRoZSBlZGdlIG9mIGEgd29yZCIpCikKCmthYmxlKHRleHRfdGFibGUsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9yZGVyX3JpZ2h0ID0gVCkgJT4lCiAgY29sdW1uX3NwZWMoMiwgaXRhbGljID0gVCwgd2lkdGggPSAiNDBlbSIpCmBgYAoKCgojIyNRdWFudGlmaWVycwoKSG93IG1hbnkgdGltZXMgd2lsbCBhIGNoYXJhY3RlciBhcHBlYXI/CgpgYGB7ciBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFLCB3YXJuaW5nID0gRkFMU0V9CnRleHRfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBFeHByZXNzaW9uID0gYygiPyIsICJcXCoiLCJcXCsiLCAie259IiwgIntuLH0iLCAieyxufSIsICJ7bixtfSIpLAogIE1lYW5pbmcgPSBjKCIwIG9yIDEiLCAiMCBvciBtb3JlIiwgIjEgb3IgbW9yZSIsICJleGFjdGx5IG4iLCAiYXQgbGVhc3QgbiIsICJhdCBtb3N0IG4iLCAiYmV0d2VlbiBuIGFuZCBtIChpbmNsdXNpdmUpIikKKQoKa2FibGUodGV4dF90YWJsZSwgImh0bWwiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKSAlPiUKICBjb2x1bW5fc3BlYygxLCBib3JkZXJfcmlnaHQgPSBUKSAlPiUKICBjb2x1bW5fc3BlYygyLCBpdGFsaWMgPSBULCB3aWR0aCA9ICI0MGVtIikKYGBgCgoKIyMjQ2xhc3NlcwoKV2hhdCBraW5kIG9mIGNoYXJhY3RlciBpcyBpdD8KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZXZhbCA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRX0KdGV4dF90YWJsZSA8LSBkYXRhLmZyYW1lKAogIEV4cHJlc3Npb24gPSBjKCJcXFxcdywgW0EtejAtOV0sIFtbOmFsbnVtOl1dIiwgIlxcXFxkLCBbMC05XSwgW1s6ZGlnaXQ6XV0iLCAiW0Etel0sIFs6YWxwaGE6XSIsICJcXFxccywgW1s6c3BhY2U6XV0iLCAiW1s6cHVuY3Q6XV0iLCAiW1s6bG93ZXI6XV0iLCAiW1s6dXBwZXI6XV0iLCAiXFxcXFcsIFteQS16MC05XSIsICJcXFxcUyIsICJcXFxcRCwgW14wLTldIiksCiAgTWVhbmluZyA9IGMoIndvcmQgY2hhcmFjdGVycyAobGV0dGVycyArIGRpZ2l0cykiLCAiZGlnaXRzIiwgImFscGhhYmV0aWNhbCBjaGFyYWN0ZXJzIiwgInNwYWNlIiwgInB1bmN0dWF0aW9uIiwgImxvd2VyY2FzZSIsICJ1cHBlcmNhc2UiLCAibm90IHdvcmQgY2hhcmFjdGVycyIsICJub3Qgc3BhY2UiLCAibm90IGRpZ2l0cyIpCikKCmthYmxlKHRleHRfdGFibGUsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9yZGVyX3JpZ2h0ID0gVCkgJT4lCiAgY29sdW1uX3NwZWMoMiwgaXRhbGljID0gVCwgd2lkdGggPSAiNDBlbSIpCmBgYAoKCiMjI09wZXJhdG9ycwoKSGVscGVyIGFjdGlvbnMgdG8gbWF0Y2ggeW91ciBjaGFyYWN0ZXJzLgoKYGBge3IgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRSwgd2FybmluZyA9IEZBTFNFfQp0ZXh0X3RhYmxlIDwtIGRhdGEuZnJhbWUoCiAgRXhwcmVzc2lvbiA9IGMoInwiLCAiLiIsICJbICBdIiwgIlsgLSBdIiwgIlteIF0iLCAiKCApIiksCiAgTWVhbmluZyA9IGMoIm9yIiwgIm1hdGNoZXMgYW55IHNpbmdsZSBjaGFyYWN0ZXIiLCAibWF0Y2hlcyBBTlkgb2YgdGhlIGNoYXJhY3RlcnMgaW5zaWRlIHRoZSBicmFja2V0cyIsICJtYXRjaGVzIGEgUkFOR0Ugb2YgY2hhcmFjdGVycyBpbnNpZGUgdGhlIGJyYWNrZXRzIiwgIm1hdGNoZXMgYW55IGNoYXJhY3RlciBFWENFUFQgdGhvc2UgaW5zaWRlIHRoZSBicmFja2V0IiwgImdyb3VwaW5nIC0gdXNlZCBmb3IgW2JhY2tyZWZlcmVuY2luZ10oaHR0cHM6Ly93d3cucmVndWxhci1leHByZXNzaW9ucy5pbmZvL2JhY2tyZWYuaHRtbCkiKQopCgprYWJsZSh0ZXh0X3RhYmxlLCAiaHRtbCIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpICU+JQogIGNvbHVtbl9zcGVjKDEsIGJvcmRlcl9yaWdodCA9IFQpICU+JQogIGNvbHVtbl9zcGVjKDIsIGl0YWxpYyA9IFQsIHdpZHRoID0gIjQwZW0iKQpgYGAKCiMjI0VzY2FwZSBjaGFyYWN0ZXJzCgpTb21ldGltZXMgYSBtZXRhLWNoYXJhY3RlciBpcyBqdXN0IGEgY2hhcmFjdGVyLiBfRXNjYXBpbmdfIGFsbG93cyB5b3UgdG8gdXNlIGEgY2hhcmFjdGVyICdhcyBpcycgcmF0aGVyIHRoYW4gaXRzIHNwZWNpYWwgZnVuY3Rpb24uIEluIFIsIHJlZ2V4IGdldHMgZXZhbHVhdGVkIGFzIGEgc3RyaW5nIGJlZm9yZSBhIHJlZ3VsYXIgZXhwcmVzc2lvbiwgYW5kIGEgYmFja3NsYXNoIGlzIHVzZWQgdG8gZXNjYXBlIHRoZSBzdHJpbmcgLSBzbyB5b3UgcmVhbGx5IG5lZWQgMiBiYWNrc2xhc2hlcyB0byBlc2NhcGUsIHNheSwgYSAnJCcgc2lnbiAoYCJcXFwkImApLiAKCmBgYHtyIGVjaG8gPSBGQUxTRSwgZXZhbCA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRX0KdGV4dF90YWJsZSA8LSBkYXRhLmZyYW1lKAogIEV4cHJlc3Npb24gPSBjKCJcXFxcIiksCiAgTWVhbmluZyA9IGMoImVzY2FwZSBmb3IgbWV0YS1jaGFyYWN0ZXJzIHRvIGJlIHVzZWQgYXMgY2hhcmFjdGVycyAoKiwgJCwgXiwgLiwgPywgfCwgXFxcXCwgWywgXSwgeywgfSwgKCwgKSkuIAogICAgICAgICAgICAgIE5vdGU6IHRoZSBiYWNrc2xhc2ggaXMgYWxzbyBhIG1ldGEtY2hhcmFjdGVyLiIpCikKCmthYmxlKHRleHRfdGFibGUsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9yZGVyX3JpZ2h0ID0gVCkgJT4lCiAgY29sdW1uX3NwZWMoMiwgaXRhbGljID0gVCwgd2lkdGggPSAiNDBlbSIpCmBgYAoKVHJvdWJsZS1zaG9vdGluZyB3aXRoIGVzY2FwaW5nIG1ldGEtY2hhcmFjdGVycyBtZWFucyBhZGRpbmcgYmFja3NsYXNoZXMgdW50aWwgc29tZXRoaW5nIHdvcmtzLiAKCiFbSm9raW5nL05vdCBKb2tpbmcgKHhrY2QpXShpbWcvYmFja3NsYXNoZXMucG5nKQoKV2hpbGUgeW91IGNhbiBhbHdheXMgcmVmZXIgYmFjayB0byB0aGlzIGxlc3NvbiBmb3IgbWFraW5nIHlvdXIgcmVndWxhciBleHByZXNzaW9ucywgeW91IGNhbiBhbHNvIHVzZSB0aGlzIFtyZWdleCBjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8wOS9SZWdFeENoZWF0c2hlZXQucGRmKS4KCjwvYnI+CgoKIyNEYXRhIENsZWFuaW5nIHdpdGggQmFzZSBSIChBS0EgV2hhdCBpcyBFbG9uIE11c2sgdXAgdG8gYW55d2F5cz8pCgpMZXQncyB0YWtlIHRoaXMgY2FjYXBob255IG9mIGNoYXJhY3RlcnMgd2UndmUganVzdCBsZWFybmVkIGFib3V0IGFuZCBwZXJmb3JtIHNvbWUgYmFzaWMgZGF0YSBjbGVhbmluZyB0YXNrcyB3aXRoIGFuIGFjdHVhbCBtZXNzeSBkYXRhIHNldC4gSSBoYXZlIHNjcmFwZWQgRWxvbiBNdXNrJ3MgbGF0ZXN0IHR3ZWV0cyBmcm9tIFR3aXR0ZXIuIFRoZSBjb2RlIHRvIGRvIHRoaXMgaXMgaW4gdGhlIExlc3NvbiA0IGZpbGUgW3R3aXR0ZXJfc2NyYXBlLlJdKGh0dHBzOi8vZ2l0aHViLmNvbS9lYWN0b24vQ0FHRUYvYmxvYi9tYXN0ZXIvTGVzc29uXzQvdHdpdHRlcl9zY3JhcGUuUikgaWYgeW91IGFyZSBjdXJpb3VzIG9yIHdhbnQgdG8gY3JlZXAgc29tZW9uZSBvbiBUd2l0dGVyLgoKTGV0J3MgcmVhZCBpbiB0aGUgc2V0IG9mIHR3ZWV0cywgdGFrZSBhIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YS4KCmBgYHtyIH0KZWxvbl90d2VldHNfZGYgPC0gcmVhZC5kZWxpbSgiZGF0YS9lbG9uX3R3ZWV0c19kZi50eHQiLCBzZXAgPSAiXHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKYGBgCgpUaGUgd2FybmluZyB3aXRoIEVPRiAoZW5kIG9mIGZpbGUpIHdpdGhpbiBxdW90ZWQgc3RyaW5nIGlzIHBvc3NpYmx5IGR1ZSB0byB0aGUgZmFjdCB0aGF0IHRoZXJlIGFyZSBzcGVjaWFsIGNoYXJhY3RlcnMgKGVtb2ppcywgYXJyb3dzLCBldGMuKSBpbnNpZGUgdGhlIGNlbGxzLiBMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgdGhlIGZpbGUgd2FzIHBhcnNlZC4KCmBgYHtyfQpzdHIoZWxvbl90d2VldHNfZGYpCgpgYGAKCk91ciBlbmQgZ29hbCBpcyBnb2luZyB0byBiZSB0byBsb29rIGF0IHRoZSB0b3AgNTAgd29yZHMgaW4gRWxvbiBNdXNrJ3MgdHdlZXRzIGFuZCBtYWtlIGEgd29yZGNsb3VkLiBJIGRvbid0IHdhbnQgdXJscywgaGFzdGFncywgb3Igb3RoZXIgdGFncy4gSSBhbHNvIGRvbid0IHdhbnQgcHVuY3R1YXRpb24gb3Igc3BhY2VzLiBJIGp1c3Qgd2FudCB0byBleHRyYWN0IHRoZSB3b3JkcyBmcm9tIHR3ZWV0cy4gSXQgbWlnaHQgYmUgZnVuIHRvIGxvb2sgYXQgdGhlIHRvcCBmYXZvcml0ZSB0d2VldHMgd2hpbGUgd2UgYXJlIGRhdGEgY2xlYW5pbmcsIHNvIGxldCdzIHVzZSBgdGlkeXZlcnNlYCBmdW5jdGlvbnMgdG8ga2VlcCB0aGUgdGV4dCB0d2VldHMgYW5kIG9yZGVyIHRoZW0gYnkgdGhlIGZhdm9yaXRlZCBjb3VudHMuCmBgYHtyfQplbG9uX3R3ZWV0c19kZiA8LSBlbG9uX3R3ZWV0c19kZiAlPiUgCiAgc2VsZWN0KHRleHQsIGZhdm9yaXRlQ291bnQpICU+JQogIGFycmFuZ2UoZGVzYyhmYXZvcml0ZUNvdW50KSkKCmVsb25fdHdlZXRzX2RmJHRleHRbMTo1XQpgYGAKCkZpcnN0LCBJIHdhbnQgdG8gcmVtb3ZlIHRoZSB0YWdzIGZyb20gdGhlIGJlZ2lubmluZyBvZiB3b3Jkcy4gSSBhbSBnb2luZyB0byBzYXZlIG15IHJlZ2V4IGV4cHJlc3Npb24gaW50byBhbiBvYmplY3QgLSBzbyB3ZSBjYW4gdXNlIHRoZW0gYWdhaW4gbGF0ZXIuCgpXaGF0IHRoaXMgZXhwcmVzc2lvbiBzYXlzIGlzIHRoYXQgSSB3YW50IHRvIGZpbmQgbWF0Y2hlcyBmb3IgYSBoYXN0YWcgT1IgYW4gYXNwZXJhbmQgKCdhdCcgc3ltYm9sKSBmb2xsb3dlZCBieSBhdCBsZWFzdCBvbmUgd29yZCBjaGFyYWN0ZXIuIGBncmVwYCBpcyBhIGZ1bmN0aW9uIHRoYXQgYWxsb3dzIHVzIHRvIG1hdGNoIG91ciBwYXR0ZXJuIChvdXIgZXhwcmVzc2lvbikgdG8gYSBjaGFyYWN0ZXIgdmVjdG9yLiBJdCBpcyBhIGdvb2QgaWRlYSB0byBkbyBhIHZpc3VhbCBpbnNwZWN0aW9uIG9mIHlvdXIgcmVzdWx0IHRvIG1ha2Ugc3VyZSB5b3VyIG1hdGNoZXMgb3Igc3Vic3RpdHV0aW9ucyBhcmUgd29ya2luZyB0aGUgd2F5IHlvdSBleHBlY3RlZC4KCmBgYHtyfQp0YWdzIDwtICIjfEBcXHcrIgoKZ3JlcChwYXR0ZXJuID0gdGFncywgeCA9IGVsb25fdHdlZXRzX2RmJHRleHQpCgpgYGAKV2UgY2FuIHNlZSB0aGF0IGBncmVwYCByZXR1cm5zIHRoZSBpbmRleCBvZiB0aGUgbWF0Y2guIFdlIGhhdmUgYSBudW1iZXIgb2YgZW50cmllcyB0aGF0IGluY2x1ZGUgdGFncy4gV2UgYWxzbyBoYXZlIGEgbnVtYmVyIG9mIHdhcm5pbmdzIHRoYXQgd2Ugd2lsbCByZXR1cm4gdG8uIAoKSWYgd2Ugd2FudCB0byByZXR1cm4gdGhlIHR3ZWV0IGl0c2VsZiBpbnN0ZWFkIG9mIHRoZSBpbmRleCwgd2UgY2FuIHVzZSB0aGUgYXJndW1lbnQgYHZhbHVlID0gVFJVRWAuICBJbiB0aGlzIGNhc2UsIGl0IGxvb2tzIGxpa2UgZWFjaCB0d2VldCBtYXRjaGVkIGRvZXMgaGF2ZSBhIHRhZy4gKFlvdSB3aWxsIGhhdmUgYSB3YXJuaW5nIGhlcmUgdG9vLCBJIGRpZG4ndCBwcmludCBpdCBoZXJlLikKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KZ3JlcCh0YWdzLCBlbG9uX3R3ZWV0c19kZiR0ZXh0LCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkKCmBgYApXZSBjYW4gdGhlbiB1c2UgYGdzdWJgIHRvIHJlcGxhY2UgdGhhdCBwYXR0ZXJuIChvdXIgdGFncykgd2l0aCBub3RoaW5nIChhbiBlbXB0eSBzdHJpbmcpLgpgYGB7cn0KZWxvbl90d2VldHNfZGYkdGV4dCA8LSBnc3ViKHBhdHRlcm4gPSB0YWdzLCByZXBsYWNlbWVudCA9ICIiLCBlbG9uX3R3ZWV0c19kZiR0ZXh0KQpgYGAKCkJhY2sgdG8gdGhlIHdhcm5pbmdzIGFib3V0IHN0cmluZ3MgYmVpbmcgJ2ludmFsaWQgaW4gdGhpcyBsb2NhbGUnLiBMZXQncyB0YWtlIGEgbG9vayBhdCB0aGVzZSBzdHJpbmdzIGJ5IHN1YnNldHRpbmcgZm9yIHRoZSBpbmRpY2VzIGdpdmVuLgoKYGBge3J9CmVsb25fdHdlZXRzX2RmJHRleHRbYygxMCwxMTgsIDE1NiwgMjE5LCAyMjQpXQoKYGBgCgpGcm9tIGNvbnRleHQsIGl0IGxvb2tzIGxpa2UgdGhlc2UgY2hhcmFjdGVyIHN0cmluZ3MgaGF2ZSBlbW9qaXMgaW4gdGhlbSwgd2hpY2ggaGF2ZSB0aGVpciBvd24gY2hhcmFjdGVyIGNvZGVzLiBXaHkgd291bGQgdGhpcyBnaXZlIHVzIGFuIGVycm9yPyBUd2VldHMgYXJlIF9lbmNvZGVkXyBpbiBVVEYtMTYgYW5kIGNvbnZlcnRlZCB0byBVVEYtOCB3aGVuIHJlYWQgaW50byBSLiBUaGluZ3MgdGhhdCBoYXZlIGNoYXJhY3RlciBjb2RlcyBnZXQgZW5jb2RlZCBkaWZmZXJlbnRseS4gSGVyZSBpcyBhbiBleGFtcGxlIG9mIFtlbW9qaSBlbmNvZGluZ10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RvZGF5LWlzLWEtZ29vZC1kYXkvRW1vdGljb25zL21hc3Rlci9lbURpY3QuY3N2KS4gU2luY2Ugd2UgYXJlIGdvaW5nIHRvIHJlbW92ZSBhbnl0aGluZyB3aXRoIHNwZWNpYWwgY2hhcmFjdGVyIGNvZGVzIChpZS4gYW4gYXBvc3Ryb3BoZSBvciBlbW9qaSksIHdlIGFyZSBnb2luZyB0byB1c2UgdGhlIGBpY29udmAgZnVuY3Rpb24gdG8gc3Vic3RpdHV0ZSBlbmNvZGVkIGNoYXJhY3RlciBjb2RlcyB0aGF0IG5lZWQgY29udmVydGluZyB3aXRoIG5vdGhpbmcgKGFnYWluLCBhbiBlbXB0eSBjaGFyYWN0ZXIgc3RyaW5nKS4gVGhpcyBpcyBub3Qgc29tZXRoaW5nIHlvdSB3aWxsIGhhdmUgdG8gZGVhbCB3aXRoIG9uIGEgZGFpbHkgYmFzaXMsIGJ1dCBjaGFyYWN0ZXIgZW5jb2RpbmcgaXMgc29tZXRoaW5nIHRvIGJlIGF3YXJlIG9mLCBlc3BlY2lhbGx5IHdoZW4gc2NyYXBpbmcgZGF0YSBmcm9tIHRoZSB3ZWIuICAKCmBgYHtyfQplbG9uX3R3ZWV0c19kZiR0ZXh0IDwtIGljb252KGVsb25fdHdlZXRzX2RmJHRleHQsICJVVEYtOCIsICJBU0NJSSIsIHN1YiA9ICIiKQoKZWxvbl90d2VldHNfZGYkdGV4dFtjKDEwLDExOCwgMTU2LCAyMTksIDIyNCldCmBgYAoKTG9va2luZyBiYWNrIGF0IG91ciBwcm9ibGVtYXRpYyBzdHJpbmdzLCB5b3UgY2FuIHNlZSB0aGF0IHRoZSBlbW9qaXMgaGF2ZSBiZWVuIHJlbW92ZWQgYXMgd2VsbCBhcyBxdW90YXRpb24gbWFya3MuIE91ciBoYXN0YWcgYW5kIGFzcGVyYW5kIHdvdWxkIGFsc28gaGF2ZSBiZWVuIGVuY29kZWQgY2hhcmFjdGVycyBoYWQgd2Ugbm90IGFscmVhZHkgcmVtb3ZlZCB0aGVtLgoKCk91ciBuZXh0IHN0ZXAgd291bGQgYmUgdG8gcmVtb3ZlIHVybHMuIFRoaXMgaXMgYSBiaXQgdHJpY2t5LiBXZSBjb3VsZCBiZSBsb29raW5nIGZvciBodHRwOi8vIG9yIGh0dHBzOi8vIGZvbGxvd2VkIGJ5IHdlIGRvbid0IGtub3cgd2hhdCAoc29tZSBjb21iaW5hdGlvbiBvZiBsZXR0ZXJzLCBudW1iZXJzIGFuZCBmb3J3YXJkIHNsYXNoZXMpLiAKCldlIGNhbiBjaGVjayBvdXQgd2hpY2ggdHdlZXRzIGhhdmUgdXJscyB1c2luZyBgZ3JlcGAgYXMgd2UgZGlkIHByZXZpb3VzbHkgdG8gc2VlIGlmIHdlIG1hbmFnZWQgdG8gbWF0Y2ggdXJscy4KCldlIGFyZSBnb2luZyB0byBjb250aW51ZSBvdXIgcGF0dGVybiBvZiB1c2luZyBgZ3N1YmAgdG8gc3Vic3RpdHV0ZSB3aGF0IHdlIGRvbid0IHdhbnQgd2l0aCBhbiBlbXB0eSBjaGFyYWN0ZXIgc3RyaW5nLgoKYGBge3J9CnVybCA8LSAiaHR0cFtzXT86Ly9bWzphbG51bTpdLlxcL10rIgoKZ3JlcCh1cmwsIGVsb25fdHdlZXRzX2RmJHRleHQsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoKQoKZWxvbl90d2VldHNfZGYkdGV4dCA8LSBnc3ViKHBhdHRlcm4gPSAiaHR0cFtzXT86Ly9bWzphbG51bTpdLlxcL10rIiwgcmVwbGFjZW1lbnQgPSAiIiwgZWxvbl90d2VldHNfZGYkdGV4dCkKCmBgYAoKV2UgY2FuIGFsc28gdXNlIGBncmVwbGAgdG8gZ2V0IGEgbG9naWNhbCByZXBvbnNlIGZvciB3aGV0aGVyIGEgdHdlZXQgaGFzIGEgdXJsIG9yIG5vdC4gVGhhdCB3YXksIGlmIHlvdSB3YW50ZWQgdG8gZ3JhYiBhbGwgb2YgdGhlIHVybHMgdGhhdCBFbG9uIE11c2sgc3VnZ2VzdHMgdG8gdmlzaXQsIHlvdSBjYW4gZmlsdGVyIHdpdGggYGdyZXBsYCB0byBzZWxlY3QgYWxsIG9mIHRoZSB0d2VldHMgd2hlcmUgaXQgaXMgVFJVRSB0aGF0IGEgdXJsIGlzIHByZXNlbnQuCgpgYGB7cn0KZ3JlcGwodXJsLCBlbG9uX3R3ZWV0c19kZiR0ZXh0KSAlPiUgaGVhZCgpCgplbG9uX3VybHMgPC0gZWxvbl90d2VldHNfZGYgJT4lIGZpbHRlcihncmVwbCh1cmwsIGVsb25fdHdlZXRzX2RmJHRleHQpKQpgYGAKCgpMYXN0bHksIHdlIGFyZSBnb2luZyB0byBnZXQgcmlkIG9mIHRyYWlsaW5nIHNwYWNlcywgbnVtYmVycywgYW5kIHB1bmN0dWF0aW9uIGFsbCBhdCB0aGUgc2FtZSB0aW1lLiBZb3UgY2FuIGZpbmQgdHJhaWxpbmcgc3BhY2VzIGF0IHRoZSB2ZXJ5IGVuZCBvZiBvdXIgdHdlZXQgc3RyaW5nIGZyb20gcmVtb3ZpbmcgdGhlIHVybHMuCgpgYGB7cn0KdHJhaWwgPC0gIlsgXSskfFswLTldKnxbWzpwdW5jdDpdXSIKCmdyZXAodHJhaWwsIGVsb25fdHdlZXRzX2RmJHRleHQsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoKQoKYGBgCldlIGNhbiBjaGVjayB0byBzZWUgdGhhdCB3ZSBhcmUgcGlja2luZyB1cCBzdHJpbmdzIHdpdGggcHVuY3R1dGF0aW9uLCBudW1iZXJzIGFuZCB0cmFpbGluZyBzcGFjZXMsIGFuZCB0aGVuIHdlIGNhbiByZW1vdmUgdGhlbSBhbmQgY29tcGFyZSBvdXIgb3V0cHV0LgoKYGBge3J9CmVsb25fdHdlZXRzX2RmJHRleHQgPC0gZ3N1YihwYXR0ZXJuID0gdHJhaWwsIHJlcGxhY2VtZW50ID0gIiIsIGVsb25fdHdlZXRzX2RmJHRleHQpCgplbG9uX3R3ZWV0c19kZiR0ZXh0WzE6NV0KYGBgCgpJdCBsb29rcyBsaWtlIGV2ZXJ5dGhpbmcgd29ya2VkIGV4Y2VwdCB0aGVyZSBhcmUgZXh0cmEgc3BhY2VzIGZyb20gd2hlbmV2ZXIgYSBudW1iZXIgd2FzIHJlbW92ZWQuIExldCdzIHRha2UgYWxsIG9mIHRoZSBwbGFjZXMgd2hlcmUgdGhlcmUgYXJlIDIgb3IgbW9yZSBzcGFjZXMgY3JlYXRlZCBhbmQgc3Vic3RpdHV0ZSB0aGVtIHdpdGgganVzdCBvbmUgc3BhY2UuIAoKYGBge3J9CnNwYWNlIDwtICJcXHN7Mix9IgoKZ3JlcChzcGFjZSwgZWxvbl90d2VldHNfZGYkdGV4dCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpCgpgYGAKQWdhaW4sIHdlIGNhbiBjaGVjayB0byBzZWUgdGhhdCB3ZSBhcmUgcGlja2luZyB1cCBzdHJpbmdzIHdpdGggZXh0cmEgc3BhY2VzLCBhbmQgdGhlbiByZXBsYWNlIHRob3NlIHNwYWNlcyB3aXRoIGEgc2luZ2xlIHNwYWNlLgoKCmBgYHtyfQplbG9uX3R3ZWV0c19kZiR0ZXh0IDwtIGdzdWIocGF0dGVybiA9IHNwYWNlLCByZXBsYWNlbWVudCA9ICIgIiwgZWxvbl90d2VldHNfZGYkdGV4dCkKCmVsb25fdHdlZXRzX2RmJHRleHRbMTo1XQpgYGAKCkl0IHdvcmtlZCEgCgoqKioKX19DaGFsbGVuZ2VfXyAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvbWF4cmVzZGVmYXVsdC5qcGcpe3dpZHRoPTIwMHB4fQoKPC9kaXY+CgpXZSBhbHNvIGhhdmUgYSBsZWFkaW5nIHdoaXRlc3BhY2Ugd2hlcmUgd2UgcmVtb3ZlZCBhIG51bWJlci4gSG93IHdvdWxkIHdlIHJlbW92ZSB0aGF0IHdoaXRlc3BhY2U/IENhbiB5b3UgdGhpbmsgb2YgbW9yZSB0aGFuIG9uZSB3YXkgdG8gZG8gdGhpcz8KCgo8L2JyPgo8L2JyPgo8L2JyPgo8L2JyPgoKKioqCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CmV4dHJhIDwtICJeWyBdIgoKZ3JlcChleHRyYSwgZWxvbl90d2VldHNfZGYkdGV4dCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpCgplbG9uX3R3ZWV0c19kZiR0ZXh0IDwtIGdzdWIocGF0dGVybiA9IGV4dHJhLCByZXBsYWNlbWVudCA9ICIiLCBlbG9uX3R3ZWV0c19kZiR0ZXh0KQoKZWxvbl90d2VldHNfZGYkdGV4dFsxOjVdCmBgYAoKT253YXJkcyEhIExldCdzIGJyZWFrIHRoZSB0d2VldHMgZG93biBpbnRvIGluZGl2aWR1YWwgd29yZHMsIHNvIHdlIGNhbiBzZWUgd2hhdCB0aGUgbW9zdCBjb21tb24gd29yZHMgdXNlZCBhcmUuIFdlIGNhbiB1c2UgdGhlIGJhc2UgUiBmdW5jdGlvbiBgc3Ryc3BsaXRgIHRvIGRvIHRoaXM7IGluIHRoaXMgY2FzZSB3ZSB3YW50IHRvIHNwbGl0IG91ciB0d2VldHMgaW50byB3b3JkcyB1c2luZyBzcGFjZXMuIAoKCmBgYHtyfQpzdHJzcGxpdChlbG9uX3R3ZWV0c19kZiR0ZXh0LCBzcGxpdCA9ICIgIikgJT4lIGhlYWQoKQoKYGBgCk5vdGUgdGhhdCB0aGUgb3V0cHV0IG9mIHRoaXMgZnVuY3Rpb24gaXMgc29tZSBob3JyaWJsZSBuZXN0ZWQgbGlzdCBvYmplY3QuIAoKTHVja2lseSB0aGVyZSBpcyBhbiBgdW5saXN0YCBmdW5jdGlvbiB3aGljaCByZWN1cnNpdmVseSB3aWxsIGdvIHRocm91Z2ggbGlzdHMgdG8gc2ltcGxpZnkgdGhlaXIgZWxlbWVudHMgaW50byBhIHZlY3Rvci4gTGV0J3MgdHJ5IGl0IGFuZCBjaGVjayB0aGUgc3RydWN0dXJlIG9mIG91ciBvdXRwdXQuIFdlIHdpbGwgc2F2ZSB0aGlzIHRvIGFuIG9iamVjdCBjYWxsZWQgJ3dvcmRzJy4KCmBgYHtyfQp1bmxpc3Qoc3Ryc3BsaXQoZWxvbl90d2VldHNfZGYkdGV4dCwgc3BsaXQgPSAiICIpKSAlPiUgaGVhZCgyMCkKCndvcmRzIDwtIHVubGlzdChzdHJzcGxpdChlbG9uX3R3ZWV0c19kZiR0ZXh0LCBzcGxpdCA9ICIgIikpCgpgYGAKCk91ciBvdXRwdXQgaXMgbm93IGEgbG9uZyBjaGFyYWN0ZXIgdmVjdG9yLiBUaGlzIHdpbGwgbWFrZSBpdCBtdWNoIGVhc2llciB0byBjb3VudCB3b3Jkcy4gCgpgYGB7cn0Kc3RyKHdvcmRzKQpgYGAKCkxldCdzIHRha2UgYSBwZWFrIGF0IHRoZSB3b3Jkcy4KYGBge3J9CnRhaWwod29yZHMpCmBgYAoKCgpHcmVhdCEgQnV0Li4uIHdlIG1pc3NlZCBzb21lIGBcbmAgKG5ld2xpbmUpIGFuZCBgXHRgICh0YWIpIGNoYXJhY3RlcnMuIFRoZXNlIGFyZSBub3QgcHVuY3R1YXRpb24gY2hhcmFjdGVycy4KCgoKKioqCl9fQ2hhbGxlbmdlX18gCgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDttYXJnaW46MCAxMHB4IDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nL21heHJlc2RlZmF1bHQuanBnKXt3aWR0aD0yMDBweH0KCjwvZGl2PgoKTmV3bGluZSBhbmQgdGFiIGNoYXJhY3RlcnMgYXJlIHNlcGFyYXRpbmcgMiB3b3Jkcy4gU3BsaXQgdGhlc2Ugd29yZHMgYXBhcnQgYW5kIGdldCByaWQgb2YgdGhlIG5ld2xpbmUgY2hhcmFjdGVyLiBDb252ZXJ0IGFsbCBvZiBvdXIgY2hhcmFjdGVyIHN0cmluZ3MgdG8gbG93ZXJjYXNlIChJIGhhdmVuJ3Qgc2hvd24geW91IGhvdyB0byBkbyB0aGlzLCBidXQgSSBiZWxpZXZlIGluIHlvdXIgZ29vZ2xlLWZ1KS4gQ2hlY2sgdGhlIGZpcnN0IGFuZCBsYXN0IDUwIHdvcmRzIHRvIHNlZSBpZiBhbnl0aGluZyBlbHNlIGlzIGFtaXNzLgoKCjwvYnI+CjwvYnI+CjwvYnI+CgoqKioKCmBgYHtyIGluY2x1ZGUgPSBGQUxTRX0Kd29yZHMgPC0gdG9sb3dlcih1bmxpc3Qoc3Ryc3BsaXQod29yZHMsICJcXG58XFx0IikpKQojZXF1aXZhbGVudCB0bwp3b3JkcyA8LSBjYXNlZm9sZCh1bmxpc3Qoc3Ryc3BsaXQod29yZHMsICJcXG58XFx0IikpLCB1cHBlciA9IEZBTFNFKQoKd29yZHNbMTo1MF0KdGFpbCh3b3JkcywgNTApCmBgYAoKVGhlcmUgYXJlIHN0aWxsIGEgZmV3IHByb2JsZW1zIHdpdGggd29yZHMgY3V0b2ZmIGxpa2UgJ3NvbHYnLCBvciAnZmxhbWV0aHJvd2VyJyBhbmQgJ2ZsYW1ldGhyb3dlcnMnIGJlaW5nIHRoZSBzYW1lIHdvcmQsIG9yICdub3J0aCcgYW5kICdrb3JlYScgYmVsb25naW5nIHRvZ2V0aGVyIGZvciBjb250ZXh0LiBJZiB3ZSB3ZXJlIHNlcmlvdXMgYWJvdXQgdGhpcyBkYXRhc2V0IHdlIHdvdWxkIG5lZWQgdG8gcmVzb2x2ZSB0aGVzZSBpc3N1ZXMuIFdlIGFsc28gaGF2ZSBzb21lIGh0bWwgYW5kIHR3aXR0ZXItc3BlY2lmaWMgdGFncyB0aGF0IHdlIHdpbGwgZGVhbCB3aXRoIHNob3J0bHkuIAoKTGV0J3MgbW92ZSBhaGVhZCBhbmQgY291bnQgdGhlIG51bWJlciBvZiBvY2N1cmVuY2VzIG9mIGVhY2ggd29yZCBhbmQgb3JkZXIgdGhlbSBieSBmcmVxdWVuY3kuIFdlIGRvIHRoaXMgdXNpbmcgb3VyIGBkcGx5cmAgZnVuY3Rpb25zIChMZXNzb24gMikuCgpgYGB7cn0KZGF0YS5mcmFtZSh3b3JkcykgJT4lIGNvdW50KGZhY3Rvcih3b3JkcykpICU+JSBhcnJhbmdlKGRlc2MobikpCmBgYAoKCldvdy4gV2UgaGF2ZSBkaXNjb3ZlcmVkIHBlb3BsZSB1c2UgcHJlcG9zaXRpb25zIGFuZCBjb25qdW5jdGlvbnMuIFRoZXJlIGFyZSBhbHNvIHdvcmRzIHVucmVsYXRlZCB0byBjb250ZW50IGJ1dCB0aGF0IGFyZSBodG1sIGphcmdvbiwgb3IgdGhpbmdzIGxpa2UgJ25hJyBhbmQgJ2ZhbHNlJy4gCgpMdWNraWx5IHRleHQgbWluaW5nIGlzIGFuIGFyZWEgb2YgZGF0YSBhbmFseXRpY3MgaW4gZnVsbCBmb3JjZSBhbmQgdGhlcmUgaXMgYSBsaXN0IG9mICdzdG9wIHdvcmRzJyB0aGF0IGNhbiBiZSB1c2VkIHRvIGdldCByaWQgb2Ygd29yZHMgdGhhdCBhcmUgdW5saWtlbHkgdG8gY29udGFpbiB1c2VmdWwgaW5mb3JtYXRpb24gYXMgcGFydCBvZiB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlLiBIb3dldmVyLCB3ZSB3aWxsIGhhdmUgdG8gYWRkIHRvIHRoaXMgbGlzdC4KClRoZSBkYXRhIHRoYXQgY29tZXMgd2l0aCB0aGUgcGFja2FnZSBpcyBjYWxsZWQgYHN0b3Bfd29yZHNgLiBXZSBjYW4gc2F2ZSBpdCBhcyBhbiBvYmplY3QgYW5kIHRha2UgYSBsb29rIGF0IGl0cyBzdHJ1Y3R1cmUuCmBgYHtyfQpzdG9wX3dvcmRzIDwtIHN0b3Bfd29yZHMKc3RyKHN0b3Bfd29yZHMpCmBgYAoKV2UgY2FuIHRoZW4gYWRkIHJvd3MgdG8gdGhpcyBkYXRhIGZyYW1lIHdpdGggd29yZHMgb3VyIG93biBzdG9wIHdvcmRzLiAgUmVtZW1iZXIgdGhhdCB0byBgYmluZF9yb3dzYCBkYXRhIGZyYW1lcyB0b2dldGhlciwgdGhlIGNvbHVtbiBuYW1lcyBoYXZlIHRvIG1hdGNoLiBXZSBjYW4gbWFrZSBhIHNtYWxsIGRhdGEgZnJhbWUgYW5kIGNhbGwgb3VyIGxleGljb24gJ2N1c3RvbScuIE5vdGUgdGhhdCBJIGhhdmUgd3JpdHRlbiAnY3VzdG9tJyBvbmNlIC0gaXQgd2lsbCByZWN5Y2xlIGFzIGEgY2hhcmFjdGVyIHZlY3RvciBvZiBsZW5ndGggMSB0byB0aGUgbGVuZ3RoIG9mIHRoZSBkYXRhIGZyYW1lLgoKYGBge3J9CmFkZF9zdG9wIDwtIGRhdGEuZnJhbWUod29yZCA9IGMoIm5hIiwgImZhbHNlIiwgImhyZWYiLCAicmVsIiwgIm5vZm9sbG93IiwgInRydWUiLCAiYW1wIiwgInR3aXR0ZXIiLCAiaXBob25lYSIsICJyZWxub2ZvbGxvd3R3aXR0ZXIiLCAicmVsbm9mb2xsb3dpbnN0YWdyYW1hIiksIAogICAgICAgICAgICAgICAgICAgICAgIGxleGljb24gPSAiY3VzdG9tIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKc3RvcF93b3JkcyA8LSBiaW5kX3Jvd3Moc3RvcF93b3JkcywgYWRkX3N0b3ApCmBgYAoKClRvIHJlbW92ZSB0aGVzZSBzdG9wIHdvcmRzIGZyb20gb3VyIGxpc3Qgb2Ygd29yZHMgZnJvbSB0d2VldHMsIHdlIHBlcmZvcm0gYW4gYW50aS1qb2luIChmcm9tIExlc3NvbiAzKS4KCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0Kd29yZHMgPC0gYW50aV9qb2luKGRhdGEuZnJhbWUod29yZHMpLCBzdG9wX3dvcmRzLCBieT1jKCJ3b3JkcyIgPSAid29yZCIpKQoKYGBgCgpMZXQncyBsb29rIGF0IG91ciB0b3Agd29yZHMgYnkgY291bnQgbm93LCBhbmQgc2F2ZSB0aGlzIG9yZGVyLgoKYGBge3J9CndvcmRzICU+JSBjb3VudCh3b3JkcykgJT4lIGFycmFuZ2UoZGVzYyhuKSkKCndvcmRzIDwtIHdvcmRzICU+JSBjb3VudCh3b3JkcykgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgonYm9yaW5nJywgJ2ZhbGNvbicsICd0ZXNsYScsICdyb2NrZXQnLCAnbGF1bmNoJywnZmxhbWV0aHJvd2VyJywgJ2NhcnMnLCAnc3BhY2V4JywgJ3R1bm5lbHMnLCBhbmQgJ21hcnMnIGFuZCAnYWknIGFyZSBhIGJpdCBmdXJ0aGVyIGRvd24gdGhlIGxpc3QuIFRoZXJlIGFyZSBhIGZldyB3b3JkcyB0aGF0IGxvb2sgbGlrZSB0aGV5IHNob3VsZCBiZSBhZGRlZCB0byB0aGUgJ3N0b3Agd29yZHMnIGxpc3QgKGRvbnQsIGRvZXNudCwgZGlkbnQsIGltKSwgYnV0IHdlJ2xsIHdvcmsgd2l0aCB0aGlzIGZvciBub3cuCgpXZSBjYW4gbWFrZSBhIHdvcmQgY2xvdWQgb3V0IG9mIHRoZSB0b3AgNTAgd29yZHMsIHdoaWNoIHdpbGwgYmUgc2l6ZWQgYWNjb3JkaW5nIHRvIHRoZWlyIGZyZXF1ZW5jeS4gSSBhbSBzdGFydGluZyB3aXRoIHRoZSBmaXJzdCB3b3JkIGFmdGVyIEVsb24gTXVzaydzIHR3aXR0ZXIgaGFuZGxlLiBUaGUgZGVmYXVsdCBjb2xvciBpcyBibGFjaywgYnV0IHdlIGNhbiB1c2Ugb3VyIGB2aXJpZGlzYCBwYWNrYWdlIChMZXNzb24gMykgdG8gaGF2ZSBhIHBsZWFzaW5nIGNvbG9yIHBhbGV0dGUuIEl0IGlzIG9rYXkgaWYgdGhpcyBjb2RlIGdpdmVzIHlvdSBhIHdhcm5pbmcgdGhhdCBub3QgYWxsIHdvcmRzIGNhbiBiZSBmaXQgb24gdGhlIHBhZ2UsIHRoaXMgY2FuIGJlIGNoYW5nZWQgYnkgYWRqdXN0aW5nIHRoZSBgc2NhbGVgIGFyZ3VtZW50LgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQp3b3Jkc1syOjUxLF0gJT4lCiAgICB3aXRoKHdvcmRjbG91ZCh3b3Jkcywgbiwgb3JkZXJlZC5jb2xvcnMgPSBUUlVFLCBjb2xvcnMgPSB2aXJpZGlzKDUwKSwgdXNlLnIubGF5b3V0ID0gVFJVRSkpCmBgYAoKKioqCgojI0RhdGEgQ2xlYW5pbmcgd2l0aCBzdHJpbmdyL3N0cmluZ2kgKEFLQSBXaGF0IGlzIFRydW1wIHVwIHRvIGFueXdheXM/KQoKV2UgYXJlIGdvaW5nIHRvIGdvIHRocm91Z2ggdGhlIHNhbWUgZGF0YSBjbGVhbmluZyBwcm9jZXNzIHdpdGggdGhlIGBzdHJpbmdyYCBwYWNrYWdlIHVzaW5nIFRydW1wJ3MgdHdlZXRzLiBUaGUgc3ludGF4IGlzIGEgbGl0dGxlIGRpZmZlcmVudCwgYnV0IGl0IGlzIHByZXR0eSBpbnR1aXRpdmUgb25jZSB5b3UgZ2V0IHN0YXJ0ZWQuIEFsbCBgc3RyaW5ncmAgZnVuY3Rpb25zIGNhbiBiZSBmb3VuZCB1c2luZyBgc3RyX2AgKyBgVGFiYC4gQWdhaW4sIHdlIHdpbGwgc3RhcnQgYnkgbG9hZGluZyB0aGUgZGF0YXNldCBhbmQgbG9va2luZyBhdCB0aGUgdG9wIDUgZmF2b3JpdGUgdHdlZXRzLiBXZSB3aWxsIHJlbW92ZSBhbGwgZW5jb2RlZCBjaGFyYWN0ZXIgY29kZXMgcmlnaHQgYXdheS4KCmBgYHtyfQp0cnVtcF90d2VldHNfZGYgPC0gcmVhZC5kZWxpbSgiZGF0YS90cnVtcF90d2VldHNfZGYudHh0Iiwgc2VwID0gIlx0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQp0cnVtcF90d2VldHNfZGYkdGV4dCA8LSBpY29udih0cnVtcF90d2VldHNfZGYkdGV4dCwgIlVURi04IiwgIkFTQ0lJIiwgc3ViID0gIiIpCgp0cnVtcF90d2VldHNfZGYgPC0gdHJ1bXBfdHdlZXRzX2RmICU+JSBzZWxlY3QodGV4dCwgZmF2b3JpdGVDb3VudCkgJT4lIGFycmFuZ2UoZGVzYyhmYXZvcml0ZUNvdW50KSkgCnRydW1wX3R3ZWV0c19kZiR0ZXh0WzE6NV0KYGBgCgpUaGUgZmlyc3QgdGhpbmcgdGhhdCB3ZSBkaWQgd2FzIGxvb2sgZm9yIHRhZ3MuIFRoZSBvcmRlciBvZiBhcmd1bWVudHMgYXJlIHN3aXRjaGVkIGluIGBzdHJpbmdyYCByZWxhdGl2ZSB0byB0aGUgYmFzZSBmdW5jdGlvbnMuIFRoZSBmaXJzdCBhcmd1bWVudCB3aWxsIGJlIHRoZSBjaGFyYWN0ZXIgc3RyaW5nIHdlIGFyZSBzZWFyY2hpbmcsIGFuZCB0aGUgc2Vjb25kIGFyZ3VtZW50IHdpbGwgYmUgdGhlIHBhdHRlcm4gd2UgYXJlIG1hdGNoaW5nLiBgc3RyX2V4dHJhY3RgIHdpbGwgcmV0dXJuIHRoZSBpbmRleCBvZiB0aGUgbWF0Y2gsIGFzIHdlbGwgYXMgdGhlIG1hdGNoLiBUaGlzIGlzIHNpbWlsYXIgdG8gYGdyZXBgIHdoZW4gYHZhbHVlID0gVFJVRWAuIE5vdGUgdGhhdCB0aGUgbWF0Y2ggaXMgZXh0cmFjdGVkIHJhdGhlciB0aGFuIHRoZSBlbnRpcmUgc3RyaW5nLgoKYGBge3J9CnN0cl9leHRyYWN0KHN0cmluZyA9IHRydW1wX3R3ZWV0c19kZiR0ZXh0LCBwYXR0ZXJuID0gdGFncykgJT4lIGhlYWQoMTAwKQoKYGBgCgpgc3RyX2RldGVjdGAgaXMgc2ltaWxhciB0byBgZ3JlcGxgIHJldHVybmluZyBUUlVFIG9yIEZBTFNFIGlmIGEgbWF0Y2ggaXMgb3IgaXNuJ3QgZm91bmQsIHJlc3BlY3RpdmVseS4KCmBgYHtyfQpzdHJfZGV0ZWN0KHRydW1wX3R3ZWV0c19kZiR0ZXh0LCB0YWdzKQoKYGBgCkxldCdzIHJlbW92ZSBvdXIgdXJscyBhcyBiZWZvcmUuIFdpdGggdGhlIGBzdHJfcmVwbGFjZWAgZnVuY3Rpb24gd2UgY2FuIHNwZWNpZnkgb3VyIHBhdHRlcm4gYW5kIHJlcGxhY2VtZW50LCBpbiB0aGlzIGNhc2UgYW4gZW1wdHkgY2hhcmFjdGVyIHN0cmluZy4gV2UgY2FuIHNlZSBpbiB0aGUgcmVzdWx0IHRoYXQgdGhlIHVybHMgaGF2ZSBiZWVuIHJlcGxhY2VkLgoKYGBge3J9CnN0cl9yZXBsYWNlX2FsbCh0cnVtcF90d2VldHNfZGYkdGV4dFsxOjEwXSwgcGF0dGVybiA9IHVybCwgcmVwbGFjZW1lbnQgPSAiIikKdHJ1bXBfdHdlZXRzX2RmJHRleHQgPC0gc3RyX3JlcGxhY2VfYWxsKHRydW1wX3R3ZWV0c19kZiR0ZXh0LCBwYXR0ZXJuID0gdXJsLCByZXBsYWNlbWVudCA9ICIiKQpgYGAKCkxldCdzIGJlIGFtYml0aW91cyBhbmQgdHJ5IHRvIHJlbW92ZSB0YWdzLCBudW1iZXJzIGFuZCBwdW5jdHVhdGlvbiBjaGFyYWN0ZXJzIGFuZCBudW1iZXJzIGFsbCBpbiBvbmUgZ28uIGBzdHJfcmVtb3ZlYCBhdXRvbWF0aWNhbGx5IHJlcGxhY2VzIHRoZSBtYXRjaCB3aXRoIGFuIGVtcHR5IGNoYXJhY3RlciBzdHJpbmcuIEl0IHR1cm5zIG91dCB0aGUgYEBgIGFuZCBgI2AgYXJlIHB1bmN0dWF0aW9uIGNoYXJhY3RlcnMsIHNvIHJlbW92aW5nIHRoZW0gaXMgdGFrZW4gY2FyZSBvZiB1c2luZyBgW1s6cHVuY3Q6XV1gLiBXZSBhbHNvIHdhbnQgdG8gcmVtb3ZlIHRoZSBtZXRhY2hhcmFjdGVyIGAkYCAod2hpY2ggaXMgbm90IGNvbnNpZGVyZWQgcHVuY3R1YXRpb24uIFdlIGFyZW4ndCBzdXJlIHdoYXQgb3JkZXIgdGhlIG51bWJlcnMgYW5kIHB1bmN0dWF0aW9uIG1pZ2h0IGNvbWUgaW4gYW5kIHNxdWFyZSBicmFja2V0cyBhbGxvdyBBTlkgY2hhcmFjdGVycyBpbnNpZGUgdGhlIGJyYWNrZXRzIHRvIGJlIG1hdGNoZWQuIFdlIGFyZSBub3Qgc3VyZSBpZiB0aGVyZSB3aWxsIGJlIHplcm8sIG9uZSwgb3IgbWFueSBvZiBvdXIgdGFyZ2V0IGNoYXJhY3RlcnMgaW4gYSB0d2VldCwgaG93ZXZlciBgc3RyX3JlbW92ZV9hbGwoKWAgd2lsbCByZW1vdmUgZXZlcnkgaW5zdGFuY2Ugb2YgdGhpcyBwYXR0ZXJuIChvdGhlcndpc2Ugd2Ugd291bGQgdXNlIHRoZSBgKmAgb3V0c2lkZSB0aGUgYnJhY2tldHMgdG8gaW5kaWNhdGUgMCBvciBtb3JlIHRpbWVzKS4gTG9va2luZyBhdCB0aGUgb3V0cHV0LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG51bWJlcnMgYW5kIHB1bmN0dWF0aW9uIGFuZCBkb2xsYXIgc2lnbnMgYXJlIGluZGVlZCByZW1vdmVkLgoKYGBge3J9CmNsZWFuX2FsbCA8LSAiW1swLTldW1s6cHVuY3Q6XV1cXCRdIgoKdHJ1bXBfdHdlZXRzX2RmJHRleHQgPC0gc3RyX3JlbW92ZV9hbGwodHJ1bXBfdHdlZXRzX2RmJHRleHQsIHBhdHRlcm4gPSBjbGVhbl9hbGwpCgp0cnVtcF90d2VldHNfZGYkdGV4dFsxOjEwXQoKYGBgCgpBcyBleHBlY3RlZCwgd2Ugc3RpbGwgaGF2ZSB0cmFpbGluZyBzcGFjZXMuIFdoaXRlc3BhY2UgY2hhcmFjdGVycyBhcmUgbm90IHZpc2libGUsIGJ1dCB0YWtlIHVwIHNwYWNlLiBOZXdsaW5lIGNoYXJhY3RlcnMsIHRhYnMgYW5kIHNwYWNlcyBhcmUgYSBmb3JtIG9mIHdoaXRlc3BhY2UuIGBzdHJpbmdyYCBoYXMgaXRzIG93biBmdW5jdGlvbiBmb3IgdHJpbW1pbmcgd2hpdGVzcGFjZSwgYHN0cl90cmltYCwgd2hpY2ggeW91IGNhbiB1c2UgdG8gc3BlY2lmeSB3aGV0aGVyIHlvdSB3YW50IGxlYWRpbmcgb3IgdHJhaWxpbmcgd2hpdGVzcGFjZSB0cmltbWVkLCBvciBib3RoLgoKYGBge3J9CnRydW1wX3R3ZWV0c19kZiR0ZXh0IDwtIHN0cl90cmltKHRydW1wX3R3ZWV0c19kZiR0ZXh0LCBzaWRlID0gImJvdGgiKQoKdHJ1bXBfdHdlZXRzX2RmJHRleHRbMToxMF0KYGBgCgpTZWUgaG93IHdlIGhhdmUgYSBjb3VwbGUgZXh0cmEgc3BhY2VzIGluIHRoZSBtaWRkbGUgb2Ygc29tZSBvZiBvdXIgc3RyaW5ncz8gYHN0cl9zcXVpc2hgIHdpbGwgdGFrZSBjYXJlIG9mIHRoYXQgZm9yIHVzLCBsZWF2aW5nIG9ubHkgYSBzaW5nbGUgc3BhY2UgYmV0d2VlbiB3b3Jkcy4KCmBgYHtyfQp0cnVtcF90d2VldHNfZGYkdGV4dCA8LSBzdHJfc3F1aXNoKHRydW1wX3R3ZWV0c19kZiR0ZXh0KQoKdHJ1bXBfdHdlZXRzX2RmJHRleHRbMToxMF0KYGBgCgpBbGwgdGhhdCdzIGxlZnQgaXMgdG8gY29udmVydCBhbGwgY2hhcmFjdGVycyB0byBsb3dlcmNhc2UsIGFuZCB0aGVuIHdlIGNhbiBzZWUgdGhlIHRvcCBUcnVtcCB3b3JkcyEKCmBgYHtyfQp0cnVtcF90d2VldHNfZGYkdGV4dCA8LSB0b2xvd2VyKHRydW1wX3R3ZWV0c19kZiR0ZXh0KQoKdHJ1bXBfdHdlZXRzX2RmJHRleHRbMToxMF0KYGBgCgpUbyBnZXQgb3VyIHR3ZWV0cyBpbnRvIGEgd29yZCBsaXN0IHdlIHVzZSBgc3RyX3NwbGl0YCwgYSBzaW1pbGFyIGZ1bmN0aW9uIHRvIGBzdHJzcGxpdGAsIHN0aWxsIHNwbGl0dGluZyBieSB0aGUgc3BhY2VzIGJldHdlZW4gd29yZHMuIFRoZSBhcmd1bWVudCBgc2ltcGxpZnkgPSBGQUxTRWAgcmV0dXJucyBhIGxpc3Qgb2YgY2hhcmFjdGVyIHZlY3RvcnMgd2hpY2ggd2UgdGhlbiB1bmxpc3QuCgoKYGBge3J9CndvcmRzIDwtIHVubGlzdChzdHJfc3BsaXQodHJ1bXBfdHdlZXRzX2RmJHRleHQsIHBhdHRlcm4gPSAiICIsIHNpbXBsaWZ5ID0gRkFMU0UpKQpzdHIod29yZHMpCmBgYAoKV2UgY2FuIG5vdyBkbyBvdXIgYGFudGlfam9pbmAgdG8gcmVtb3ZlICdzdG9wIHdvcmRzJywgYW5kIHRhbGx5IG91ciByZW1haW5pbmcgd29yZHMgYW5kIG9yZGVyIHRoZW0gYnkgZGVzY2VuZGluZyBjb3VudHMuCgoKYGBge3Igd2FybmluZyA9IEZBTFNFfQp3b3JkcyA8LSBhbnRpX2pvaW4oZGF0YS5mcmFtZSh3b3JkcyksIHN0b3Bfd29yZHMsIGJ5PWMoIndvcmRzIiA9ICJ3b3JkIikpCgp3b3JkcyAlPiUgY291bnQod29yZHMpICU+JSBhcnJhbmdlKGRlc2MobikpIAoKd29yZHMgPC0gd29yZHMgJT4lIGNvdW50KHdvcmRzKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKYGBgCgpIbW1tLi4uIGl0IGxvb2tzIGxpa2Ugd2UgaGF2ZSB0aG9zZSBodG1sIHRhZ3MgaW4gYSBkaWZmZXJlbnQgZm9ybWF0LiBJdCdzIGludGVyZXN0aW5nIHRvIG5vdGUgdGhlc2UgbGl0dGxlIHZhcmlhdGlvbnMgYmVjYXVzZSBubyBtYXR0ZXIgaG93IG11Y2ggeW91IHRyeSB0byBhdXRvbWF0ZSB5b3VyIGFuYWx5c2lzIHRoZXJlIGlzIGFsd2F5cyBnb2luZyB0byBiZSBzb21ldGhpbmcgZnJvbSB5b3VyIG5ldyBkYXRhc2V0IHRoYXQgZGlkbid0IGZpdCB3aXRoIHlvdXIgb2xkIGRhdGFzZXQuIFRoaXMgaXMgd2h5IHdlIG5lZWQgdGhlc2UgZGF0YSB3cmFuZ2xpbmcgc2tpbGxzLiBFdmVuIHRob3VnaCBzb21lIHBhY2thZ2VzIG1heSBoYXZlIGJlZW4gY3JlYXRlZCB0byBoZWxwIHVzIG9uIG91ciB3YXksIHRoZXkgY2FuJ3QgcG9zc2libHkgY292ZXIgZXZlcnkgY2FzZS4gCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvMTQ2NzQ4MV8yNDA0MzQ5MjYxMjQyMzJfNTUwMzEwNzcyX24uanBnKXt3aWR0aD01MDBweH0KCgo8L2Rpdj4KCjwvYnI+CjwvYnI+ICAgICAKPC9icj4KPC9icj4KPC9icj4KPC9icj4gICAgIAo8L2JyPgo8L2JyPgo8L2JyPgoKPC9icj4KPC9icj4KPC9icj4KPC9icj4gICAgIAo8L2JyPgo8L2JyPgoKCgpXZSBjb3VsZCBnbyBiYWNrIGFuZCBnZXQgcmlkIG9mIHNvbWUgb2YgY2hhcmFjdGVycyBzdWNoIGFzIGA8YCwgaG93ZXZlciB3ZSBkb24ndCB3YW50IHRvIGxvc2Ugc2lnaHQgdGhhdCB0aGVzZSBhcmUgaHRtbCB0YWdzIGFuZCBub3Qgd29yZHMgKHRoZSB0d2VldCB3YXMgZnJvbSBhbiBpcGFkIG9yIGlwaG9uZSwgdGhlICd3b3JkJyBpc24ndCBiZWluZyBtZW50aW9uZWQpLiBXZSB3aWxsIGluc3RlYWQgYWRkIHRoZXNlIHRvIG91ciBzdG9wIHdvcmRzIGxpc3QuCgpgYGB7cn0KYWRkX3N0b3AgPC0gZGF0YS5mcmFtZSh3b3JkID0gYygicmVsPW5vZm9sbG93PnR3aXR0ZXIiLCAiaHJlZj0iLCAiaXBob25lPGE+IiwgIjxhIiwiZG9udCIsICIkIiwgImhyZWY9ZG93bmxvYWRpcGFkIiwgImlwYWQ8YT4iICksIAogICAgICAgICAgICAgICAgICAgICAgIGxleGljb24gPSAiY3VzdG9tIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKCnN0b3Bfd29yZHMgPC0gYmluZF9yb3dzKHN0b3Bfd29yZHMsIGFkZF9zdG9wKQoKYGBgCgpXZSB0aGVuIHBlcmZvcm0gYW4gYGFudGlfam9pbmAgd2l0aCBvdXIgbmV3IGxpc3QgYW5kIHZpZXcgdGhlIHVwZGF0ZWQgdmVyc2lvbi4gKGB3b3Jkc2Agd2FzIGFscmVhZHkgc29ydGVkIGFuZCBzbyB3ZSBkbyBub3QgbmVlZCB0byBkbyB0aGF0IGFnYWluLikKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0Kd29yZHMgPC0gYW50aV9qb2luKGRhdGEuZnJhbWUod29yZHMpLCBzdG9wX3dvcmRzLCBieT1jKCJ3b3JkcyIgPSAid29yZCIpKQp3b3Jkc1sxOjUwLF0KYGBgCidwcmVzaWRlbnQnLCAncGVvcGxlJywgJ2Zha2UnLCAnbmV3cycsICdkYWNhJywgZGVtb2NyYXRzJywgJ2pvYnMnLCAnb2JhbWEnLCAnYm9yZGVyJywgJ2ZiaScsICdjb2xsdXNpb24nLCAncnVzc2lhJywgJ3dhbGwnLCAnbWV4aWNvJyBhbmQgZnVydGhlciBkb3duIGlzICdjcm9va2VkJyBhbmQgJ2hpbGxhcnknLiAKClRydW1wJ3Mgd29yZGNsb3VkIG1pbnVzIGhpcyB0d2l0dGVyIGhhbmRsZS4KYGBge3J9CndvcmRzWzI6NTEsXSAlPiUKICAgIHdpdGgod29yZGNsb3VkKHdvcmRzLCBuLCBvcmRlcmVkLmNvbG9ycyA9IFRSVUUsIGMoMywuNSksY29sb3JzID0gdmlyaWRpcyg1MCksIHVzZS5yLmxheW91dCA9IFRSVUUpKQpgYGAKCioqKgpfX0NoYWxsZW5nZV9fIAokCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvbWF4cmVzZGVmYXVsdC5qcGcpe3dpZHRoPTIwMHB4fQoKPC9kaXY+CgpQaWNrIG9uZSBvZiB0aGUgb3RoZXIgdHdlZXQgZGF0YSBzZXRzOiAKCiAgW0JpbGwgTnllXShodHRwczovL2dpdGh1Yi5jb20vZWFjdG9uL0NBR0VGL2Jsb2IvbWFzdGVyL0xlc3Nvbl80L2RhdGEvbnllX3R3ZWV0c19kZi50eHQpLCBbSnVzdGluIFRydWRlYXVdKGh0dHBzOi8vZ2l0aHViLmNvbS9lYWN0b24vQ0FHRUYvYmxvYi9tYXN0ZXIvTGVzc29uXzQvZGF0YS9qdF90d2VldHNfZGYudHh0KSwgW1RoZSBEYWlseSBTaG93XShodHRwczovL2dpdGh1Yi5jb20vZWFjdG9uL0NBR0VGL2Jsb2IvbWFzdGVyL0xlc3Nvbl80L2RhdGEvZGFpbHlfdHdlZXRzX2RmLnR4dCksIFtLYXR5IFBlcnJ5XShodHRwczovL2dpdGh1Yi5jb20vZWFjdG9uL0NBR0VGL2Jsb2IvbWFzdGVyL0xlc3Nvbl80L2RhdGEva2F0eV90d2VldHNfZGYudHh0KSwgW0ppbW15IEZhbGxvbl0oaHR0cHM6Ly9naXRodWIuY29tL2VhY3Rvbi9DQUdFRi9ibG9iL21hc3Rlci9MZXNzb25fNC9kYXRhL2ppbW15X3R3ZWV0c19kZi50eHQpLCBbU3RlcGhlbiBDb2xiZXJ0XShodHRwczovL2dpdGh1Yi5jb20vZWFjdG9uL0NBR0VGL2Jsb2IvbWFzdGVyL0xlc3Nvbl80L2RhdGEvY29sYmVydF90d2VldHNfZGYudHh0KS4gICAgICAKCgpDbGVhbiBpdC4gUmVtb3ZlIGFsbCBvZiB0aGUgc3RvcCB3b3Jkcy4gV2VyZSB0aGVyZSBhbnkgb3RoZXIgY2hhbGxlbmdlcyBjb21wYXJlZCB0byB0aGUgcHJldmlvdXMgZGF0YXNldHM/IERpZCB5b3UgaGF2ZSB0byBjcmVhdGUgbmV3IHN0b3Agd29yZHMgb3IgZG8gZXh0cmEgcmVnZXg/IE1ha2UgYSB3b3JkY2xvdWQgb2YgdGhlIHRvcCA1MCB3b3Jkcy4KCgo8L2JyPgoKKioqCgoKIyNSbWFya2Rvd24gYW5kIGtuaXRyCgpNYXJrZG93biBpcyBhIHBsYWluIHRleHQgZm9ybWF0dGluZyBzeW50YXguIEl0IGFsbG93cyBvbmUgdG8gZWFzaWx5IGFkZCBoZWFkaW5ncywgbGlzdHMsIGxpbmtzLCBoaWdobGlnaHRpbmcsIGJ1bGxldHMsIGltYWdlcywgZXF1YXRpb25zLCB0YWJsZXMgYW5kIHRleHQgc3R5bGluZy4gUiBoYXMgYSBtb2RpZmllZCB2ZXJzaW9uIG9mIG1hcmtkb3duIChSIG1hcmtkb3duKSB3aGVyZSB5b3UgY2FuIGVtYmVkIGNvZGUgX2NodW5rc18gaW50byBhIGRvY3VtZW50LiBDb21iaW5lZCB3aXRoIHRoZSBga25pdHJgIHBhY2thZ2UsIHRoaXMgYWxsb3dzIHVzIHRvIG1ha2UgcmVwcm9kdWNpYmxlIGRvY3VtZW50cy4gVGhlIGF3ZXNvbWVuZXNzIG9mIHRoaXMgY29tYmluYXRpb24gYWxsb3dzIHVzIHRvIGFubm90YXRlIG91ciBjb2RlIHdoaWxlIHdlIHdvcmsgaW4gYSBmb3JtYXQgdGhhdCBpcyBpbW1lZGlhdGVseSBwcmVzZW50YWJsZS4gV2l0aCB0aGUgY2xpY2sgb2YgYSBidXR0b24gb3VyIHNjcmlwdCAob3Igbm90ZWJvb2spIGNhbiBiZSBjb252ZXJ0ZWQgaW50byBhIHdvcmQgZG9jdW1lbnQsIGEgcGRmLCBvciBhIHNoYXJlYWJsZSBodG1sIGh5cGVybGluay4gT3RoZXIgZm9ybWF0cyBzdWNoIGFzIHNsaWRlcyBhcmUgYWxzbyBwb3NzaWJsZS4gVGhpcyBtZWFucyB0aGF0IHlvdSBvbmx5IGhhdmUgdG8gZG8geW91ciB3b3JrIG9uY2UgLSB5b3UgZG9uJ3QgaGF2ZSB0byBoYXZlIHlvdXIgY29kZSwgZ2VuZXJhdGUgaW1hZ2VzLCBwYXN0ZSB0aGVtIGludG8gcG93ZXJwb2ludCBvciB3b3JkIC0gZ2V0IGFza2VkIHRvIGNoYW5nZSBzb21ldGhpbmcsIHJlcnVuIHRoZSBjb2RlLCBnZXQgdGhlIGZpZ3VyZSwgY2hhbmdlIHlvdXIgcG93ZXJwb2ludC4uLiBldmVyeXRoaW5nIGlzIGFsbCBpbiBvbmUgcGxhY2UgLSB5b3UgbWFrZSB5b3VyIGNoYW5nZSwga25pdCB5b3VyIGRvY3VtZW50IGFuZCB5b3UgYXJlIGRvbmUgLSB5b3UgZG9uJ3QgaGF2ZSB0byBsZWF2ZSBSU3R1ZGlvLgoKTWFya2Rvd24gYW5kIGBrbml0cmAgY2FuIGFsc28gc2F2ZSB1cyB0aW1lIHNjcm9sbGluZyB0aHJvdWdoIG91ciBjb2RlIC0gYSB0YWJsZSBvZiBjb250ZW50cyBjYW4gYmUgYWRkZWQsIGNvZGUgY2h1bmtzIGNhbiBiZSBuYW1lZCAtIGJvdGggb2Ygd2hpY2ggYWxsb3cgdXMgdG8ganVtcCBhcm91bmQgYW5kIG5hdmlnYXRlIG91ciBzY3JpcHQgZWFzaWx5LiBJdCB0YWtlcyBhIGJpdCBvZiBkaXNjaXBsaW5lIHRvIGdldCBzdGFydGVkLCBidXQgSSBiZWxpZXZlIHRoYXQgeW91IHdpbGwgc2VlIHRoZSBiZW5lZml0cyBwcmV0dHkgcXVpY2tseS4gVGhlcmUgYXJlIGV2ZW4gbWFya2Rvd24gdGVtcGxhdGVzIGZvciBzdWJtaXR0aW5nIHRvIGRpZmZlcmVudCBqb3VybmFscyBvciB3cml0aW5nIGEgdGhlc2lzLgoKRm9yIHRoaXMgbGVzc29uIEkgc3VnZ2VzdCBnb2luZyB0byBgVG9vbHMgLT4gR2xvYmFsIE9wdGlvbnMgLT4gUiBNYXJrZG93biAtPiBTaG93IG91dHB1dCBwcmV2aWV3IGluYDogYW5kIGNoYW5nZSBmcm9tICdXaW5kb3cnIHRvICdWaWV3ZXIgUGFuZScsIHRoZW4gY2xpY2sgJ0FwcGx5JyBhbmQgJ09LJy4gVGhpcyB3aWxsIGFsbG93IHVzIHRvIHNlZSBvdXIgbmV3IGRvY3VtZW50IGluIHRoZSBzYW1lIHdpbmRvdyAoaW4gdGhlIFZpZXdlcikgaW5zdGVhZCBvZiBzd2l0Y2hpbmcgYmFjayBhbmQgZm9ydGggYmV0d2VlbiBvdXIgY29kZSBhbmQgdGhlIGRvY3VtZW50IGluIGEgc2VwYXJhdGUgd2luZG93LiAKCiMjI1IgbWFya2Rvd24gc3ludGF4CgpMZXQncyBzdGFydCBieSBjcmVhdGluZyBhIG5ldyBSIG1hcmtkb3duIGRvY3VtZW50IGJ5IGdvaW5nIHRvIGBGaWxlIC0+IE5ldyBGaWxlIC0+IFIgTWFya2Rvd25gLiBSIHdpbGwgYXNrIHlvdSBmb3IgdGhlIFRpdGxlIG9mIHlvdXIgZG9jdW1lbnQsIHRoZSBBdXRob3IgYW5kIHdoZXRoZXIgeW91IHdhbnQgdG8gX3JlbmRlcl8geW91ciBtYXJrZG93biBhcyBodG1sLCBwZGYsIG9yIGFzIGEgd29yZCBkb2N1bWVudC4gVGhlcmUgaXMgYWxzbyB0aGUgb3B0aW9uIHRvIG1ha2Ugc2xpZGVzICh1bmRlciBQcmVzZW50YXRpb24pLCBhIFNoaW55IGFwcCwgb3IgdXNlIFRlbXBsYXRlcyBmb3IgcGFja2FnZSBkb2N1bWVudGF0aW9uIG9yIEdpdEh1YiAodGhlcmUgaXMgYSBnaXQgdmVyc2lvbiBvZiBtYXJrZG93biB0aGF0IGRpZmZlcnMgc2xpZ2h0bHkgZnJvbSBSIG1hcmtkb3duKS4gIGh0bWwgcmVuZGVycyBmYXN0ZXIgdGhhbiB0aGUgb3RoZXIgZm9ybWF0cywgc28gd2Ugd2lsbCBzdGljayB3aXRoIHRoYXQgYW5kIGNsaWNrIE9LLiBSIGltbWVkaWF0ZWx5IHB1dHMgYSB5YW1sICh5ZXQgYW5vdGhlciBtYXJrdXAgbGFuZ3VhZ2UsIHlhbWwgYWluJ3QgbWFya3VwIGxhbmd1YWdlKSBoZWFkZXIgd2hpY2ggdGVsbHMgeW91IGhvdyB0aGUgZmlsZSBpcyBjb25maWd1cmVkLiAKCiAgICAtLS0KICAgIHRpdGxlOiAiUiBtYXJrZG93biBMZXNzb24iCiAgICBhdXRob3I6ICJFQSIKICAgIGRhdGU6ICJBcHJpbCAxMSwgMjAxOCIKICAgIG91dHB1dDogaHRtbF9kb2N1bWVudAogICAgLS0tCgpBcyB5b3UgY2FuIHNlZSwgdGhlIGRhdGUgaGFzIGJlZW4gYWRkZWQgKHRoaXMgaXMgdGhlIGRhdGUgb2Ygc2NyaXB0IGNyZWF0aW9uIGFuZCBkb2VzIG5vdCB1cGRhdGUgd2hlbiB0aGUgc2NyaXB0IGlzIHJlbmRlcmVkKSwgYW5kIHRoZSBvdXRwdXQgaXMgZ29pbmcgdG8gYmUgaHRtbC4gTm90ZSBhbHNvIHRoYXQgeW91ciBkb2N1bWVudCBpcyBVbnRpdGxlZC4gV2UgY2FuIGdvIGFoZWFkIGFuZCBzYXZlIHRoYXQgYXMgJ1JtYXJrZG93bl9MZXNzb24nIGFuZCBzZWUgdGhhdCB0aGUgZmlsZSBpcyBzYXZlZCBhcyBhIGAuUm1kYCBmaWxlLgoKV2UgY2FuIHNlZSB0aGF0IFIgaGFzIGEgbGl0dGxlIGRlbW8gc2V0IHVwIGFscmVhZHkgd2hpY2ggd2UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aC4gVGhlIG5ldyBlbGVtZW50IGluIC5SbWQgZmlsZXMgYXJlIHRoZXNlIGNvZGUgX2NodW5rc18gZGVub3RlZCBieSBhIHNldCBvZiAzIGJhY2t0aWNrcywgZm9sbG93ZWQgYnkgYHtyfWAsIHNvbWUgY29kZSBhbmQgYSBjbG9zaW5nIHNldCBvZiAzIGJhY2t0aWNrcy4gVGhlIGtleWJvYXJkIHNob3J0Y3V0IGZvciBnZW5lcmF0aW5nIGEgY29kZSBjaHVuayBpcyBgQ1RSTCArIEFMVCArIElgLgoKXGBcYFxgXHsgciBuYW1lX29mX2NodW5rLCAgICBjb2RlX29wdGlvbnNcfSAgICAgCgp0eXBlIGNvZGUgaGVyZQoKXGBcYFxgCgpUaGVzZSBjb2RlIGNodW5rcyBoYXZlIGJlZW4gbmFtZWQgJ3NldHVwJywgJ2NhcnMnLCBhbmQgJ3ByZXNzdXJlJywgYW5kIGF0IHRoZSBib3R0b20gbGVmdCB0aGUgc291cmNlIHBhbmUgKG9yIGlmIHlvdSBnbyB0byBgQ29kZSAtPiBKdW1wIFRvLi4uYCBpdCB3aWxsIHBvcCB1cCkgeW91IGNhbiBuYXZpZ2F0ZSBiZXR3ZWVuIHRoZXNlIGNvZGUgY2h1bmtzLiBUaGlzIGlzIGEgaGVscGZ1bCBmZWF0dXJlIGFzIHlvdXIgY29kZSBnZXRzIGEgYml0IGxvbmdlci4gCgpZb3UgaGF2ZSBwcm9iYWJseSBhbHNvIG5vdGljZWQgdGhhdCBpbiB0aGlzIG5hdmlnYXRpb24gYmFyIHRoZSBib2xkZWQgaXRlbXMgY29ycmVzcG9uZCB0byB0aGUgdGl0bGUgb2YgdGhlIGRvY3VtZW50IGFuZCB0aGUgdGV4dCB3aXRoIGEgbGVhZGluZyBgIyNgLiBIYXNodGFncyAod2hlbiBvdXRzaWRlIGNvZGUgY2h1bmtzLCBpZS4gaW4gbWFya2Rvd24gbGFuZ3VhZ2UpIGRlbm90ZSBoZWFkZXJzLiBUaGUgbnVtYmVyIG9mIGhhc2h0YWdzIGRlbm90ZXMgdGhlIGxldmVsIG9mIHRoZSBoZWFkZXIgYXMgd2VsbCBhcyB0aGUgc2l6ZS4gRm9yIGV4YW1wbGUsIHRoZSB0aXRsZSBpcyBhIGZpcnN0IGxldmVsIGhlYWRlciwgYW5kICdSIE1hcmtkb3duJyBhbmQgJ0luY2x1ZGluZyBQbG90cycgYXJlIHNlY29uZCBsZXZlbCBoZWFkZXJzLCBhbmQgd2lsbCBhbHNvIGJlIHNtYWxsZXIgdGhhbiB0aGUgdGl0bGUuIExldCdzIGdvIGFoZWFkIGFuZCBfa25pdF8gb3VyIGRvY3VtZW50IGJ5IGNsaWNraW5nIHRoZSBLbml0IGJ1dHRvbi4gTm90ZSB0aGF0IHlvdSBjYW4gY2hhbmdlIGZyb20geW91ciBkZWZhdWx0IG91dHB1dCBjaG9pY2UgKGh0bWwpIHRvIFdvcmQgb3IgcGRmIGluIHRoZSBkcm9wZG93biBtZW51LgoKTGV0J3MgbG9vayBhdCBob3cgbWFya2Rvd24gaXMgcmVuZGVyZWQgaW4gb3VyIGh0bWwgZmlsZS4gV2UgY2FuIHNlZSB0aGF0IHRvICoqYm9sZCoqIHRleHQgeW91IGNhbiBoYXZlIHR3byBhc3RlcmlrcyAoKiopIG9yIHR3byB1bmRlcnNjb3JlcyAoX18pIG9uIGVpdGhlciBzaWRlIG9mIHRoZSB0ZXh0LiAKCllvdSBjYW4gaW5zZXJ0IGEgdXJsIGJ5IHR5cGluZyB0aGUgdXJsIGluc2lkZSBvZiBhcnJvdyBicmFja2V0cyA8PGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20+Pi4gSWYgeW91IHdhbnQgdGhlIGxpbmsgdG8gYmUgbmFtZWQgJ3JtYXJrZG93bicgY2FuIGZvcm1hdCBpdCBsaWtlIHRoaXMgW3JtYXJrZG93bl0gKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIHdpdGhvdXQgdGhlIHNwYWNlIGluYmV0d2VlbiB0aGUgbmFtZSBhbmQgdGhlIHVybC4gUmVwbGFjZSB0aGUgdXJsIHdpdGggdGhlIG5hbWVkIHZlcnNpb24gYW5kIGNsaWNrIGtuaXQgdG8gc2VlIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBvdXRwdXQuCgpUaGUgb3RoZXIgZW1waGFzaXplZCB0ZXh0IGluIHRoaXMgZG9jdW1lbnQgaGFzIGEgZ3JleSBiYWNrZ3JvdW5kLiBUaGlzIGlzIGFjaGlldmVkIGJ5IGZsYW5raW5nIHRoZSB0ZXh0IHdpdGggXGBgYmFja3RpY2tzYFxgLiBUaGUgY29kZSBpbiB0aGUgJ2NhcnMnIGNodW5rIGhhcyBhIGdyZXkgYmFja2dyb3VuZCBhbmQgdGhlIGV2YWx1YXRlZCBvdXRwdXQgaXMgaW4gd2hpdGUuIFRoaXMgaXMgc3RhbmRhcmQgaW4gdGhlIFIgY29tbXVuaXR5IGZvciB0aGUgY29kZSB0byBiZSBncmV5IGFuZCB0aGUgb3V0cHV0IHRvIGJlIHdoaXRlIGFuZCBjb21tZW50ZWQuIElubGluZSBjb2RlIGNhbiBiZSB3cml0dGVuIGJ5IHVzaW5nIGB7ciAyKzJ9YCBhbmQgaXQgd2lsbCBhbHNvIGhhdmUgYSBncmV5IGJhY2tncm91bmQuIAoKCipSbWFya2Rvd24qCmBgYHtyIHJlc3VsdHMgPSAnYXNpcycsIGV2YWwgPSBGQUxTRX0KVG8gbWFrZSBhIGJ1bGxldGVkIGxpc3Q6CgoqIHlvdSBuZWVkIHRvIGxlYXZlIGEgbGluZSBiZWZvcmUgCiogdGhlIHRleHQgYW5kIHRoZSBzdGFydCBvZiB5b3VyIGJ1bGxldHMgYW5kIGEgCiogc3BhY2UgYmV0d2VlbiB0aGUgYXN0ZXJpayAoYnVsbGV0KSBhbmQgeW91ciB0ZXh0LgpgYGAKCgoqUmVuZGVyZWQqCgpUbyBtYWtlIGEgYnVsbGV0ZWQgbGlzdDoKCiogeW91IG5lZWQgdG8gbGVhdmUgYSBsaW5lIGJlZm9yZSAKKiB0aGUgdGV4dCBhbmQgdGhlIHN0YXJ0IG9mIHlvdXIgYnVsbGV0cyBhbmQgYSAKKiBzcGFjZSBiZXR3ZWVuIHRoZSBhc3RlcmlrIChidWxsZXQpIGFuZCB5b3VyIHRleHQuCgoKKlJtYXJrZG93biArIFJlbmRlcmVkKgoKVG8gbWFrZSBhIG51bWJlcmVkIGxpc3Q6CgoxLiB5b3UgbmVlZCB0byBsZWF2ZSBhIGxpbmUgYmVmb3JlIAoyLiB0aGUgdGV4dCBhbmQgdGhlIHN0YXJ0IG9mIHlvdXIgbnVtYmVycyBhbmQgYSAKMy4gc3BhY2UgYmV0d2VlbiB0aGUgbnVtYmVycyBhbmQgeW91ciB0ZXh0LgoKCgoqUm1hcmtkb3duKgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJywgZXZhbD1GQUxTRX0KVG8gbWFrZSBhIHN1cGVyLWNvb2wgdXBkYXRhYmxlIG51bWJlcmVkIGxpc3Q6CgoxLiB5b3UgbmVlZCB0byBkbyB0aGUgYWJvdmUgYXMgd2l0aCBudW1iZXJlZCBsaXN0cwoxLiBidXQgYWxsIG9mIHRoZSBudW1iZXJzIGFyZSBudW1iZXJlZCAnMS4nIAoxLiB5b3UgY2FuIG5vdyBhZGQsIHJlbW92ZSBhbmQgcmVvcmRlciBhbmQgeW91ciBudW1iZXJzIHdpbGwgdXBkYXRlLgoKCmBgYAoKCgoqUmVuZGVyZWQqCgpUbyBtYWtlIGEgc3VwZXItY29vbCB1cGRhdGFibGUgbnVtYmVyZWQgbGlzdDoKCjEuIHlvdSBuZWVkIHRvIGRvIHRoZSBhYm92ZSBmb3IgbnVtYmVyZWQgbGlzdHMKMS4gYnV0IGFsbCBvZiB0aGUgbnVtYmVycyBhcmUgbnVtYmVyZWQgJzEuJyAKMS4geW91IGNhbiBub3cgYWRkLCByZW1vdmUgYW5kIHJlb3JkZXIgYW5kIHlvdXIgbnVtYmVycyB3aWxsIHVwZGF0ZS4KCgoqUm1hcmtkb3duKgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJywgZXZhbD1GQUxTRX0KSWYgZXZlciB5b3VyIHRleHQKaXMgY2x1bXBpbmcgdG9nZXRoZXIKd2hlbiB5b3UgZG8gbm90IGV4cGVjdCBpdCB0bywKcmVtZW1iZXIgdGhhdCB5b3UgbmVlZCA1IHNwYWNlcyBhdCBhdCB0aGUgZW5kIG9mIGEgbGluZQp0byBzdGFydCBhIG5ldyBsaW5lLgpgYGAKCipSZW5kZXJlZCoKCklmIGV2ZXIgeW91ciB0ZXh0CmlzIGNsdW1waW5nIHRvZ2V0aGVyCndoZW4geW91IGRvIG5vdCBleHBlY3QgaXQgdG8sCnJlbWVtYmVyIHRoYXQgeW91IG5lZWQgNSBzcGFjZXMgYXQgYXQgdGhlIGVuZCBvZiBhIGxpbmUKZm9yIGEgbmV3IGxpbmUgdG8gc3RhcnQuCgoqUm1hcmtkb3duKgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJywgZXZhbD1GQUxTRX0KICAgIEEgdGV4dCBib3ggY2FuIGJlIGNyZWF0ZWQgYnkgaW5kZW50aW5nIHdpdGggVGFiIHR3aWNlLgpgYGAKCipSZW5kZXJlZCoKCiAgICBBIHRleHQgYm94IGNhbiBiZSBjcmVhdGVkIGJ5IGluZGVudGluZyB3aXRoIFRhYiB0d2ljZS4KCipSbWFya2Rvd24qCgpBIGxpbmUgYWNyb3NzIHRoZSBwYWdlIGlzICcqKionLgoKKlJlbmRlcmVkKgoKKioqCgojIyNLbml0ciBDaHVuayBPcHRpb25zCgpZb3UgbWlnaHQgbm90aWNlIHRoYXQgd2hpbGUgdGhlcmUgYXJlIDMgY29kZSBjaHVua3MgaW4gdGhpcyBleGFtcGxlLCB0aGVyZSBpcyBvbmUgbGluZSBvZiBjb2RlIHZpc2libGUgaW4gdGhlIHJlbmRlcmVkIHZlcnNpb24sIGFuZCAyIG91dHB1dHMgKHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgYSBwbG90KS4gV2h5IGRvbid0IHdlIHNlZSB0aGUgY29kZSB1c2VkIHRvIG1ha2UgdGhlIHBsb3Q/IENvZGUgY2h1bmtzIGhhdmUgb3B0aW9ucyB0aGF0IGNhbiBiZSBlbnRlcmVkIHRvIG1vZGlmeSB0aGVpciBvdXRwdXQuIEluIHRoaXMgY2FzZSB0aGUgaW5jbHVzaW9uIG9mIGBlY2hvID0gRkFMU0VgIHByZXZlbnRzIHRoZSBjb2RlIGZyb20gYmVpbmcgaW5jbHVkZWQsIGJ1dCB0aGUgY29kZSBpcyBzdGlsbCBydW4gYW5kIHNvIHRoZSBwbG90IGlzIHN0aWxsIHByb2R1Y2VkLiBUcnkgY2hhbmdpbmcgdGhlIGNvZGUgY2h1bmsgb3B0aW9uIGZvciB0aGUgcGxvdCB0byBgZXZhbCA9IEZBTFNFYC4gV2hhdCBoYXBwZW5lZD8KCmB7ciBwcmVzc3VyZSwgZXZhbCA9IEZBTFNFfWAKYGBge3IgcHJlc3N1cmUsIGV2YWwgPSBGQUxTRX0KcGxvdChwcmVzc3VyZSkKYGBgCgpgZXZhbCA9IEZBTFNFYCBtZWFucyB0aGUgY29kZSBpcyBzaG93biwgYnV0IG5vdCBldmFsdWF0ZWQuIAoKWW91IGNhbiBzcGVjaWZ5IHdoaWNoIGxpbmVzIG9mIGNvZGUgaW4gYSBjaHVuayBnZXQgZXZhbHVhdGVkLiBGb3IgZXhhbXBsZSBpZiB5b3UgaGFkIDUgbGluZXMgb2YgY29kZSwgYnV0IG9ubHkgd2FudGVkIHRvIHJ1biB0aGUgZmlyc3QgYW5kIHRoaXJkLCB5b3UgY291bGQgdXNlIGBldmFsID0gYygxLDMpYC4gVGhpcyBmZWF0dXJlIG9mIHVzaW5nIGEgdmVjdG9yIG9mIHBvc2l0aW9uIHRvIHNwZWNpZnkgY29kZSBpcyBhdmFpbGFibGUgZm9yIG90aGVyIGNodW5rIG9wdGlvbnMgc3VjaCBhcyBgZWNob2AuIAoKVGhlIGZpcnN0IGNvZGUgY2h1bmsgaW4gdGhpcyBzY3JpcHQgaXMgc2V0dGluZyBkZWZhdWx0IG9wdGlvbnMgZm9yIGFsbCBjb2RlIGNodW5rcyB0byBiZSB1c2VkIGluIHRoaXMgc2NyaXB0LiAgSW4gdGhpcyBjYXNlIGBlY2hvID0gVFJVRWAgd2FzIHNldCBhcyBhIGRlZmF1bHQgY2h1bmsgb3B0aW9uLiBUaGUgb3B0aW9uIGBpbmNsdWRlID0gRkFMU0VgIGZvciB0aGlzIGNodW5rIG1lYW5zIHRoYXQgdGhlIGNvZGUgd2lsbCBub3QgYmUgaW5jbHVkZWQsIGJ1dCB0aGUgY29kZSB3aWxsIHN0aWxsIGJlIHJ1bi4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGlzIGNvbW1hbmQgYW5kIGBlY2hvID0gRkFMU0VgIGlzIHRoYXQgdGhlIG91dHB1dCBvZiB0aGUgY29kZSBpcyBOT1Qgc2hvd24uIAoKTGV0J3MgbG9vayBhdCB3aGF0IHRoZSBkZWZhdWx0IGNodW5rIG9wdGlvbnMgYXJlIC0gdGhpcyB3YXkgd2Ugd2lsbCBiZSBhYmxlIHRvIHNlZSBhbGwgb2YgdGhlIG9wdGlvbnMgYXZhaWxhYmxlIHRvIGNoYW5nZS4KCmB7ciBzZXR1cCwgZWNobyA9IFRSVUUgfWAKYGBge3Igc2V0dXAsIGVjaG8gPSBUUlVFIH0Kc3RyKGtuaXRyOjpvcHRzX2NodW5rJGdldCgpKQpgYGAKVGhlcmUgYXJlIGEgdG9uIG9mIG9wdGlvbnMgaGVyZS4gWW91IGNhbiBndWVzcyB0aGF0IHNvbWUgb2YgdGhlbSBoYXZlIHRvIGRvIHdpdGggZGVmYXVsdCBmaWd1cmUgc2l6ZXMgYW5kIGxhYmVscywgYW5kIHRoZXJlIGEgYnVuY2ggb2Ygb3B0aW9ucyB0aGF0IGFyZSBub3Qgc3BlY2lmaWVkIChOVUxMKS4gCgpBcyBmYXIgYXMgc2V0dGluZyBjaHVuayBvcHRpb25zIGF0IHRoZSBiZWdpbm5pbmcgb2YgYSBzY3JpcHQgZ29lcywgY29uc2lkZXIgdGhlIGZvbGxvd2luZzoKCmB7cn1gCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmBgYAoKSWYgd2UgYXJlIGNyZWF0aW5nIGEgZG9jdW1lbnQsIHdlIG1heSB3YW50IHRvIHNob3cgd2hhdCBwYWNrYWdlIHdlIHVzZWQsIGJ1dCB3ZSBkb24ndCB3YW50IGFsbCBvZiB0aGUgcGFja2FnZSBzdGFydHVwIG1lc3NhZ2VzLiBXaXRoIGBtZXNzYWdlID0gRkFMU0VgIHRoZSBjb2RlIHdpbGwgcnVuIGFuZCBiZSBzaG93biBidXQgYW55IG1lc3NhZ2VzIGdlbmVyYXRlZCB3aWxsIGJlIHN1cHByZXNzZWQuCgpge3IgbWVzc2FnZSA9IEZBTFNFfWAKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmBgYAoKSSBjb3VsZCB1c2UgYGluY2x1ZGUgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFYCBpZiBJIHdhbnRlZCB0aGUgbGlicmFyeSBsb2FkZWQgYnV0IGRpZG4ndCB3YW50IHRoZSBjb2RlIG9yIGl0cyBtZXNzYWdlIHRvIGJlIHNlZW4uCgoKSWYgSSB3YW50ZWQgdG8gZG8gc29tZXRoaW5nIHNpbGx5IGxpa2UgYWRkIDYgdG8gZXZlcnkgc3VtbWFyeSB2YWx1ZSAoaW4gdHJ1dGggZWFjaCBvZiB0aGVzZSBzdW1tYXJ5IHZhbHVlcyBpcyBhIGNoYXJhY3RlcikgaXQgd291bGQgZ2VuZXJhdGUgYW4gZXJyb3IuIEEgZG9jdW1lbnQgd2lsbCBub3QgYmUgcmVuZGVyZWQgaWYgaXQgaGFzIGFuIGVycm9yIGluIGl0LiBUcnkgdG8ga25pdCB0aGUgZG9jdW1lbnQgd2l0aCB0aGlzIGNvZGUuCgpge3IgZXJyb3IgPSBUUlVFfWAKYGBge3IgZXJyb3IgPSBUUlVFfQpzdW1tYXJ5KGNhcnMpICsgNgoKYGBgCkFkZGluZyB0aGUgb3B0aW9uIGBlcnJvciA9IFRSVUVgIGFsbG93cyB0aGUgZG9jdW1lbnQgdG8gYmUgcmVuZGVyZWQgZGVzcGl0ZSB0aGUgZXJyb3IuIFRoZSBlcnJvciBtZXNzYWdlIHdpbGwgc3RpbGwgYmUgc2hvd24uCgoqKioKX19DaGFsbGVuZ2VfXyAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvbWF4cmVzZGVmYXVsdC5qcGcpe3dpZHRoPTIwMHB4fQoKPC9kaXY+CgoKRm9yIHRoZSBjb2RlIGNodW5rIGNvbnRhaW5pbmcgYHBsb3QocHJlc3N1cmUpYDogSG93IHdvdWxkIHlvdSBzaG93IGp1c3QgdGhlIGNvZGUgKG5vdCB0aGUgcGxvdCkgYW5kIG5vdCBydW4gdGhlIGNvZGU/ICBIb3cgd291bGQgeW91IHNob3cganVzdCB0aGUgY29kZSBhbmQgaGF2ZSB0aGUgb3V0cHV0IHJ1biBidXQgbm90IHNob3cgaXQ/IEhvdyB3b3VsZCB5b3UgY2hhbmdlIHRoZSBiYWNrZ3JvdW5kIGNvbG9yIHRvIHNvbWV0aGluZyBvdGhlciB0aGFuIGdyYXk/IFlvdSBjYW4gdXNlIGhlbHAgcGFnZXMsIEdvb2dsZSwgb3IgdGhlIGtuaXRyIGRvY3VtZW50YXRpb24gZm91bmQgaGVyZTogPGh0dHBzOi8veWlodWkubmFtZS9rbml0ci9vcHRpb25zLyNjaHVua19vcHRpb25zPgoKPC9icj4KPC9icj4KCioqKgoKIyMjQ2FjaGluZwoKCkluIHRoZSAnUnVuJyBkcm9wZG93biBtZW51IChmb3VuZCBpbiB0aGUgdG9wIHJpZ2h0IG9mIHRoZSBzb3VyY2UgcGFuZSksIHRoZXJlIGFyZSB2YXJpb3VzIG9wdGlvbnMgZm9yIHJ1bm5pbmcgeW91ciBjdXJyZW50IGNodW5rIC0gYENUUkwrU0hJRlQrRU5URVJgLCB0aGUgbmV4dCBjaHVuayAtIGBDVFJMK0FMVCtOYCwgYWxsIGNodW5rcyBhYm92ZSAtIGBDVFJMK0FMVCtQYCwgYWxsIGNodW5rcyBiZWxvdywgYW5kIGFub3RoZXIgZmV3IG9wdGlvbnMuIFRoaXMgYWxsb3dzIHlvdSB0byBhc3Nlc3MgdGhlIHVwc3RyZWFtIGFuZCBkb3duc3RyZWFtIGNvbnNlcXVlbmNlcyBvZiBhIGNoYW5nZSBpbiB5b3VyIGNvZGUuIGBrbml0cmAgYWxzbyBoYXMgdGhlIG9wdGlvbiB0byBfY2FjaGVfIHRoZSBvdXRwdXQgb2YgY29kZSBjaHVua3MgYnkgc2V0dGluZyB0aGUgb3B0aW9uIGBjYWNoZSA9IFRSVUVgLiBBIGZvbGRlciB3aWxsIGJlIGNyZWF0ZWQgdGhhdCBzYXZlcyB0aGUgb3V0cHV0IG9mIHlvdXIgY2h1bmsgaW4gYSBkYXRhIGZpbGUuIGBrbml0cmAgYWNjZXNzZXMgdGhlIGNhY2hlIGFuZCBsb2FkcyB0aGUgcmVzdWx0IGZyb20gdGhlIGxhc3QgdGltZSB0aGUgY2h1bmsgd2FzIHJ1biB3aXRob3V0IHJlY2FsY3VsYXRpbmcgdmFsdWVzLiAgIFRoaXMgY2FuIGJlIHZlcnkgdXNlZnVsIGlmIHRoZSBjb2RlIGluIGEgcGFydGljdWxhciBjaHVuayB0YWtlcyBhd2hpbGUgdG8gcnVuIGFuZCB5b3UgYXJlIGFzc2Vzc2luZyBjaGFuZ2VzIHVucmVsYXRlZCB0byB0aGF0IGNvZGUsICBvciBjaGFuZ2VzIGFmdGVyIHRoYXQgY29kZS4gCgpGb3IgZXhhbXBsZSwgaWYgeW91ciBkb2N1bWVudCBpc24ndCBrbml0dGluZyBiZWNhdXNlIG9mIGFuIGVycm9yIGF0IGxpbmUgMjAwIGFuZCB5b3VyIHRpbWUgaW50ZW5zaXZlIGNvZGUgcnVucyBhdCBsaW5lIDEwMCwgeW91IGNhbiBjYWNoZSB0aGUgbGluZSAxMDAgY2h1bmsgYW5kIHRyb3VibGVzaG9vdCB0aGUgbGluZSAyMDAgY29kZSB3aXRob3V0IGhhdmluZyB0byB3YWl0IGZvciB0aGlzIGVhcmxpZXIgY2h1bmsgdG8gcnVuIGFnYWluLiBUaGUgY2FjaGluZyBjYXZlYXQgaXMgdGhhdCBjaGFuZ2luZyBhbnl0aGluZyBpbiBlYXJsaWVyIGNvZGUgKGF0IGxpbmUgNTAgaW4gdGhpcyBleGFtcGxlKSB0aGF0IHlvdXIgY2FjaGVkIGNodW5rIGRlcGVuZHMgb24gd291bGQgbm90IGJlIGFwcHJvcHJpYXRlbHkgdXBkYXRlZCAoaWUuIHRoZSBjb2RlIGF0IGxpbmUgMTAwIHdvdWxkIHN0aWxsIG5vdCBjaGFuZ2UpLiBUaGVyZWZvcmUgaXQgaXMgaW1wb3J0YW50IHRvIGJlIGNvbnNjaW91cyBvZiB3aGF0IHlvdSBhcmUgY2FjaGluZyBhbmQgd2hlcmUgY2hhbmdlcyBhcmUgb2NjdXJyaW5nIGluIHlvdXIgc2NyaXB0LiBZb3Ugc2hvdWxkIHVuY2FjaGUgeW91ciBjb2RlIGNodW5rIGZvciB0aGUgZmluYWwgcmVuZGVyaW5nIHRvIG1ha2Ugc3VyZSB0aGVyZSBoYXZlbid0IGJlZW4gYW55IHVuZm9yc2VlbiBjaGFuZ2VzIHRvIHlvdXIgZG9jdW1lbnQuIAoKVGhpcyBpcyBhIHNpbXBsaWZpZWQgZXhwbGFuYXRpb24gb2YgY2FjaGluZyBhbmQgbW9yZSBkZXRhaWxzIGNhbiBiZSBmb3VuZCBpbiB0aGUgW2tuaXRyIG1hbnVhbF0oaHR0cHM6Ly93d3cuY3MuYmhhbS5hYy51ay9+YXhqL3B1Yi90ZWFjaGluZy8yMDE2LTcvc3RhdHMva25pdHItbWFudWFsLnBkZikgYW5kIGl0cyBbY2FjaGUgZGVtb10oaHR0cHM6Ly95aWh1aS5uYW1lL2tuaXRyL2RlbW8vY2FjaGUvKS4KCl9fUGxheWluZyB3aXRoIENhY2hpbmdfXwoKX19Ob3RlIHRvIERhdmlkOiBUaGVzZSBjYWNoaW5nIHNjZW5hcmlvcyB3aWxsIHBhcnRseSBiZSBvbiBTb2NyYXRpdmUuIFRoZXJlIHdpbGwgYmUgbXVsdGlwbGUgY2hvaWNlIGFuc3dlcnMgYWJvdXQgdGhlIG91dHB1dCBhbmQgc3R1ZGVudHMgdm90ZSBvbiB3aGljaCBpcyBjb3JyZWN0LiBUaGlzIHdpbGwgYWxsb3cgbWUgdG8gdHJhY2sgY29tcHJlaGVuc2lvbiBhY3Jvc3MgdGhlIHNlcmllcy4gVGhlcmVmb3JlIHRoaXMgdGV4dCB3aWxsIGJlIGluIHRoZSBvbmxpbmUgdmVyc2lvbiBvZiB0aGUgbm90ZXMuX18KCl9fU2NlbmFyaW8gMV9fCgpXaXRoIG91ciBjdXJyZW50IC5SbWQgZmlsZSwgbGV0J3Mgc2F5IHRoZSAnc3VtbWFyeScgY2h1bmsgdG9vayBhd2hpbGUgdG8gcnVuLiBMZXQncyBhZGQgYGNhY2hlID0gVFJVRWAgdG8gaXRzIGNodW5rIG9wdGlvbnMuIE1ha2Ugc3VyZSBgZXZhbCA9IEZBTFNFYCBoYXMgYmVlbiByZW1vdmVkIGZyb20gdGhlIG9wdGlvbnMgaW4geW91ciAncGxvdCcgY2h1bmsuIEtuaXQgdGhlIGRvY3VtZW50IGFuZCB0YWtlIGEgbG9vay4gCgpXZSBhcmUgbm93IGdvaW5nIHRvIGNoYW5nZSB0aGUgcGxvdCBjaHVuayB0byBkZXBlbmQgb24gdGhlIGNhcnMgZGF0YXNldC4KCgpge3IgY2Fyc31gCmBgYHtyIGV2YWwgPSBGQUxTRX0KcGxvdChjYXJzJHNwZWVkLCBjYXJzJGRpc3QpCmBgYAoKV2UgY2FuIGtuaXQgdGhlIGRvY3VtZW50IGFnYWluLCBhbmQgYXNzdW1lIHRoYXQgd2Ugc2F2ZWQgb3Vyc2VsdmVzIHRoZSB0aW1lIGNvc3Qgb2YgcnVubmluZyB0aGUgc2Vjb25kIGNodW5rIHdoZW4gd2UgYXJlIGp1c3QgdXBkYXRpbmcgYSBwbG90LiAKCk5vdyBsZXQncyBwdXQgYSBjaHVuayBfYmVmb3JlXyBvdXIgY2FjaGVkIGNodW5rIGNhbGxlZCAnbmV3IHJvdycuICBBZGQgYSBwb2ludCB0byB0aGUgY2FycyBkYXRhc2V0IHVzaW5nIGBkcGx5cmAncyBgYmluZF9yb3dzYC4gTm90ZSB0aGF0IEkgaGF2ZW4ndCBsb2FkZWQgdGhlIGVudGlyZSBgZHBseXJgIHBhY2thZ2UsIGJ1dCByYXRoZXIgaGF2ZSBqdXN0IG1hZGUgYSBjYWxsIHRvIG9uZSBzcGVjaWZpYyBmdW5jdGlvbi4gS25pdCB0aGUgZG9jdW1lbnQgYWdhaW4uICAKCmB7ciBuZXcgcm93fWAKYGBge3IgZXZhbCA9IEZBTFNFfQpjYXJzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoY2FycywgYyhzcGVlZCA9IDUwLCBkaXN0ID0gMjAwKSkKYGBgCgpUaGlzIHBvaW50IGlzIGFuIG91dGxpZXIsIGFuZCB0aGUgY2hhbmdlIGNhbiBiZSBzZWVuIG9uIHRoZSBvdXRwdXQgb2Ygb3VyIHBsb3QuIEhvd2V2ZXIsIG91ciBzdW1tYXJ5IGFsc28gZGVwZW5kcyBvbiB0aGUgY2FycyBkYXRhc2V0IGFuZCBoYXMgbm90IGJlZW4gdXBkYXRlZCAoaWUuIHRoZSBtYXhpbXVtIGRpc3RhbmNlIGlzIHN0aWxsIDEyMCBrbSkuIElmIHRoZSBjb2RlIG9mIHRoZSBjYWNoZWQgY2h1bmsgZG9lcyBub3QgY2hhbmdlLCB0aGUgY2h1bmsgaXMgbm90IHJlcnVuLgoKX19TY2VuYXJpbyAyX18KCklmIEkgY2hhbmdlIHRoZSBjYWNoZWQgY2h1bmssIHNheSwgYnkgYWRkaW5nIGFub3RoZXIgb3V0bGllciBkYXRhIHBvaW50IC0gd2hhdCBkbyB5b3UgdGhpbmsgd2lsbCBoYXBwZW4/IEFkZCB0aGlzIHBvaW50IGFuZCBrbml0IGFnYWluLgoKYHtyIGNhcnMsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFfWAKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KY2FycyA8LSBkcGx5cjo6YmluZF9yb3dzKGNhcnMsIGMoc3BlZWQgPSAxMDAsIGRpc3QgPSAzMDApKQpzdW1tYXJ5KGNhcnMpCmBgYAoKU2luY2UgdGhlIGNvZGUgaW4gdGhlIGNhY2hlIGNoYW5nZWQsIGl0cyB2YWx1ZXMgd2VyZSByZWNhbGN1bGF0ZWQgYW5kIHRoZSBtYXggZGlzdGFuY2UgaW4gdGhlIHN1bW1hcnkgaGFzIGNoYW5nZWQgdG8gMzAwIGttLiBDaGFuZ2VzIGluIHRoZSB0aGUgY2FjaGVkIGNodW5rIGFyZSBldmFsdWF0ZWQgYW5kIHBhc3NlZCBvbiB0byB0aGUgbmV4dCBjb2RlIGJsb2NrLiBUaGUgcHJldmlvdXMgY2FjaGUgdmFsdWVzIGFyZSBkZWxldGVkIGFuZCByZXBsYWNlZCB3aXRoIHRoZSBjdXJyZW50IHZhbHVlcy4gVGhlIHBsb3QsIHdoaWNoIGRlcGVuZHMgb24gdGhlIGNhcnMgZGF0YXNldCwgbm93IGhhcyBhIHBvaW50IGF0IGRpc3QgPSAzMDAga20gQU5EIGRpc3QgPSAyMDAga20uCgoKX19TY2VuYXJpbyAzX18KCgpJZiBJIGNoYW5nZSBjYXJzICh3aXRoIHRoZSBvdXRsaWVyIHBvaW50KSBpbiB0aGUgJ25ldyByb3cnIGNodW5rIGJhY2sgdG8gY2FycyAod2l0aG91dCB0aGUgb3V0bGllciBwb2ludCkgYW5kIGtuaXQgdGhlIGRvY3VtZW50LCB3aGF0IGRvIHlvdSBleHBlY3QgdG8gaGFwcGVuPwoKYHtyIG5ldyByb3d9YApgYGB7ciBuZXcgcm93fQpjYXJzIDwtIGNhcnMKYGBgCgpTaW5jZSB0aGUgY29kZSBmb3Igb3VyIGNhY2hlZCBjaHVuayBkb2Vzbid0IGNoYW5nZSBpdHMgdmFsdWVzIGFyZSBsb2FkZWQgZnJvbSB0aGUgY2FjaGUuIFRoaXMgaW5jbHVkZXMgdGhlIGNhcnMgZGF0YXNldCwgYW5kIHNvIHRoZSBwbG90LCBkb3duc3RyZWFtIG9mIG91ciBjYWNoZSwgZG9lcyBub3QgcmVmbGVjdCB0aGUgY2hhbmdlIHRvIHRoZSAnbmV3IHJvdycgY2h1bmsuCgoKX19TY2VuYXJpbyA0X18KCldoYXQgaWYgd2UgaW5zdGVhZCBjb21tZW50IHRoZSBvdXRsaWVyIHBvaW50IGluIHRoZSBjYWNoZWQgY2h1bmsgYW5kIGtlZXAgdGhlIGV4dHJhIHBvaW50IGZyb20gdGhlIGZpcnN0IGNodW5rPyAKCmB7ciBuZXcgcm93fWAKYGBge3IgfQpjYXJzIDwtIGNhcnMKY2FycyA8LSBkcGx5cjo6YmluZF9yb3dzKGNhcnMsIGMoc3BlZWQgPSA1MCwgZGlzdCA9IDIwMCkpCmBgYAoKCmB7ciBjYXJzLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRX1gCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFLCBldmFsID0gRkFMU0V9CiNjYXJzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoY2FycywgYyhzcGVlZCA9IDEwMCwgZGlzdCA9IDMwMCkpCnN1bW1hcnkoY2FycykKYGBgCgpUaGUgY29kZSBpbiB0aGUgY2FjaGUgY2hhbmdlZCBhbmQgc28gdGhlIHN1bW1hcnkgd2FzIHJlY2FsY3VhdGVkIHVzaW5nIHRoZSBlYXJsaWVyIGNodW5rLiBCb3RoIHRoZSBzdW1tYXJ5IGFuZCB0aGUgcGxvdCBzaG93IHRoZSAyMDAga20gZGlzdGFuY2UgdmFsdWUuCgpfX1NjZW5hcmlvIDVfXwoKV2hhdCBkbyB5b3UgZXhwZWN0IHRvIGhhcHBlbiBpZiBib3RoIG91dGxpZXIgcG9pbnRzIGFyZSBjb21tZW50ZWQgb3V0IGFuZCB3ZSByZXN0YXJ0IHRoZSBSIHNlc3Npb24gYW5kIGtuaXQgdGhlIGRvY3VtZW50PyBXaGF0IGlzIHRoZSBtYXggZGlzdCB2YWx1ZSBpbiB0aGUgc3VtbWFyeSB0YWJsZT8gV2hhdCBhYm91dCB0aGUgcGxvdCAtIGRvZXMgaXQgY29udGFpbiB0aGUgb3V0bGllcj8KCmB7ciBuZXcgcm93fWAKYGBge3IgfQpjYXJzIDwtIGNhcnMKI2NhcnMgPC0gZHBseXI6OmJpbmRfcm93cyhjYXJzLCBjKHNwZWVkID0gNTAsIGRpc3QgPSAyMDApKQpgYGAKCmB7ciBjYXJzLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRX1gCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFfQojY2FycyA8LSBkcGx5cjo6YmluZF9yb3dzKGNhcnMsIGMoc3BlZWQgPSAxMDAsIGRpc3QgPSAzMDApKQpzdW1tYXJ5KGNhcnMpCmBgYAoKU2luY2UgdGhlIGNvZGUgaW4gdGhlIGNhY2hlZCBjaHVuayBkaWQgbm90IGNoYW5nZWQsIGl0IGFjY2Vzc2VzIHRoZSBzdW1tYXJ5IGZyb20gdGhlIGNhY2hlZCBkYXRhLCB3aGljaCBoYXMgMjAwIGttIGFzIHRoZSBtYXhpbXVtIHZhbHVlLiBIb3dldmVyLCB0aGUgcGxvdCBuZWVkcyB0byBiZSBjcmVhdGVkIGZyb20gc2NyYXRjaCBhbmQgdXNlcyBjYXJzIGZyb20gdGhlIGZpcnN0IGNodW5rIHNvIGhhcyBhIG1heCBvZiAxMjAga20uCgpfX1NjZW5hcmlvIDZfXwoKV2hhdCBpZiB3ZSByZW1vdmUgYGNhY2hlPVRSVUVgIGZyb20gb3VyIGNvZGUgY2h1bms/Cgpge3IgY2FycywgbWVzc2FnZSA9IEZBTFNFfWAKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9CiNjYXJzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoY2FycywgYyhzcGVlZCA9IDEwMCwgZGlzdCA9IDMwMCkpCnN1bW1hcnkoY2FycykKYGBgCgpUaGUgY2FjaGUgaXMgbm8gbG9uZ2VyIGFjY2Vzc2VkLiBUaGUgc3VtbWFyeSB0YWJsZSBhbmQgdGhlIHBsb3QgcmVmbGVjdCB0aGUgb3JpZ2luYWwgY2FycyBkYXRhc2V0IHdpdGggYSBtYXggb2YgMTIwIGttLgoKX19TY2VuYXJpbyA3X18KCldoYXQgaWYgd2UgYWRkIGBjYWNoZT1UUlVFYCB0byBvdXIgY29kZSBjaHVuayBhZ2Fpbj8KCmB7ciBjYXJzLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRX1gCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFLCBldmFsID0gRkFMU0V9CiNjYXJzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoY2FycywgYyhzcGVlZCA9IDEwMCwgZGlzdCA9IDMwMCkpCnN1bW1hcnkoY2FycykKYGBgCgpUaGUgY2FjaGUgZmlsZXMgYXJlIGtlcHQgb24gdGhlIGNvbXB1dGVyLiBUaGUgc3VtbWFyeSB0YWJsZSByZWZsZWN0cyB0aGUgcHJldmlvdXNseSBjYWNoZWQgdmFsdWUgb2YgMjAwIGttLCB0aGUgcGxvdCBpcyBjYWxjdWxhdGVkIGZyb20gdGhlIGZpcnN0IGNodW5rIGFuZCBoYXMgYSBtYXggb2YgMTIwIGttLiBUaGUgY2FjaGUgd2lsbCBzdGF5IHRoaXMgdmFsdWUgdW50aWwgaXQgaXMgdXBkYXRlZC4gVG8gc3RhcnQgd2l0aCBhIGZyZXNoIGNhY2hlIGluIHRoZSBzYW1lIGRpcmVjdG9yeSB5b3UgbmVlZCB0byBkZWxldGUgeW91ciBjYWNoZWQgZmlsZXMuIAoKIyMjVGFibGVzCgpZb3UgY2FuIG1ha2UgdGFibGUgaW4gbWFya2Rvd24sIGJ1dCBpdCBpcyBraW5kIG9mIGFubm95aW5nIGNvbXBhcmVkIHRvIHVzaW5nIHRoZSBga2FibGVgIHBhY2thZ2UgdG8gbWFrZSB0YWJsZXMgaW4gYGtuaXRyYC4gTWFraW5nIHRhYmxlcyBpbiBtYXJrZG93biBpbnZvbHZlcyB1c2luZyBhIHNlcmllcyBvZiBwaXBlcyAofCkgdG8gbWFrZSBjb2x1bW5zIGFuZCBoeXBlbnMgKC0pIHRvIG1ha2UgY29sdW1uIGhlYWRlcnMuIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiBhIG1hcmtkb3duIHRhYmxlLiBIZXJlIGlzIGFuIGV4YW1wbGUgb2YgaG93IHRvIGZvcm1hdCBtYXJrZG93biB0YWJsZXM6IDxodHRwczovL2hlbHAuZ2l0aHViLmNvbS9hcnRpY2xlcy9vcmdhbml6aW5nLWluZm9ybWF0aW9uLXdpdGgtdGFibGVzLz4uIEdvIG51dHMuIAoKICAgICAgCiAgICAgIHxTdW1tYXJ5ICAgICAgfCBWYWx1ZXN8CiAgICAgIHwtLS0tLS0tLS0tLS0tfC0tLS0tLS18CiAgICAgIHwgY29ycmVsYXRpb24gfCAgIGByIGNvcihjYXJzJHNwZWVkLCBjYXJzJGRpc3QpYHwKICAgICAgfCBtZWFuIGttL2ggICB8ICAgIGByIG1lYW4oY2FycyRzcGVlZClgfAogICAgICB8IG1lYW4ga20gICAgIHwgICAgYHIgbWVhbihjYXJzJGRpc3QpYHwKICAgICAKCgoKfFN1bW1hcnkgICAgICB8IFZhbHVlc3wgICAgIAp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwgICAgIAp8IGNvcnJlbGF0aW9uIHwgICBgciBjb3IoY2FycyRzcGVlZCwgY2FycyRkaXN0KWB8ICAgICAKfCBtZWFuIGttL2ggICB8ICAgIGByIG1lYW4oY2FycyRzcGVlZClgfCAgICAgCnwgbWVhbiBrbSAgICAgfCAgICBgciBtZWFuKGNhcnMkZGlzdClgfCAgICAgCiAgICAgIAojIyMjUm91bmRpbmcgVmFsdWVzCgpUaGUgdmFsdWUgZm9yICdjb3JyZWxhdGlvbicgaW4gdGhpcyB0YWJsZSB3YXMgcmVhbGx5IHRoZSBvdXRwdXQgb2YgJ3IgY29yKGNhcnNcJHNwZWVkLCBjYXJzXCRkaXN0KScgd2hpY2ggZ2l2ZXMgdGhlIHZhbHVlIG9mIGByIGNvcihjYXJzJHNwZWVkLCBjYXJzJGRpc3QpYC4gVG8gcm91bmQgdmFsdWVzIGlzIGFzIGVhc3kgYXMgc2VsZWN0aW5nIHRoZSBudW1iZXIgb2Ygc2lnbmlmaWNhbnQgZGlnaXRzIHVzaW5nIHRoZSBgcm91bmRgIGZ1bmN0aW9uOiAnciByb3VuZChjb3IoY2Fyc1wkc3BlZWQsIGNhcnNcJGRpc3QpLCAyKScgd291bGQgdGhlbiBnaXZlIGByIHJvdW5kKGNvcihjYXJzJHNwZWVkLCBjYXJzJGRpc3QpLCAyKWAgdHdvIHNpZ25pZmljYW50IGRpZ2l0cy4gVGhlIGBrYWJsZWAgdGFibGVzIHdlcmUgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aCBoYXZlIGEgZGlnaXRzIGFyZ3VtZW50IHdoaWNoIGdldHMgcGFzc2VkIHRvIHRoZSBgcm91bmRgICBmdW5jdGlvbiAodXNhZ2U6IGBkaWdpdHMgPSAyYCkuCgojIyMjS2FibGUgdGFibGVzIGluIGtuaXRyCgpJbiB0aGlzIGxlc3NvbiB3ZSBhcmUgZ29pbmcgdG8gZm9jdXMgb24gbmljZSBsb29raW5nIGBrYWJsZWAgdGFibGVzLCB3aGljaCBhcmUgZWFzaWx5IGN1c3RvbWl6YWJsZSB0aHJvdWdoIHRoZSBba2FibGVFeHRyYV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2thYmxlRXh0cmEvdmlnbmV0dGVzL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sI2dldHRpbmdfc3RhcnRlZCkgcGFja2FnZS4gVGhlIHN1bW1hcnkgc3RhdHMgZnJvbSBjYXJzIGlzIGEgdGFibGUuIEhvd2V2ZXIsIGl0IGRvZXNuJ3QgbG9vayB2ZXJ5IGdvb2QuIEhlcmUgaXMgYSByZW1pbmRlciBvZiB0aGUgZGVmYXVsdCBvdXRwdXQuIAoKYGBge3J9CnN1bW1hcnkoY2FycykKYGBgCgpUaGlzIHN1bW1hcnkgaXMgYW4gb2RkIHRhYmxlIG9iamVjdC4gSWYgd2UgdHVybiBpdCBpbnRvIGEgZGF0YSBmcmFtZSBpdCB3aWxsIGJlIGVhc2llciB0byB3b3JrIHdpdGguCgpgYGB7cn0KZGF0IDwtIGRhdGEuZnJhbWUoc3BlZWQgPSBzdW1tYXJ5KGNhcnMpWywxXSwgZGlzdGFuY2UgPSBzdW1tYXJ5KGNhcnMpWywyXSkKCmBgYAoKSGVyZSwgYSBzaW1wbGUgY2FsbCB0byBga2FibGVgIGNyZWF0ZXMgYSB0YWJsZSBzdHlsZWQgc2ltaWxhciB0byB0aGUgYWJvdmUgbWFya2Rvd24uIAoKYGBge3J9CmthYmxlKGRhdCkKYGBgCgpBIHZhcmlldHkgb2Ygc3R5bGVzIGFyZSBvZmZlcmVkIHdpdGggc2ltcGxlIHN5bnRheC4gSGVyZSB3ZSBoYXZlIHN0cmlwZWQgcm93cyB3aGljaCBoaWdobGlnaHQgd2hlbiB5b3UgaG92ZXIgb3ZlciB0aGVtLCB0aGUgdGFibGUgd2lkdGggaWlzIHRoZSBsZW5ndGggb2YgdGhlIGxvbmdlc3QgdGV4dCBhbmQgbm90IGFjcm9zcyB0aGUgd2hvbGUgcGFnZSwgYW5kIHRoZSB0YWJsZSBpcyBsZWZ0LWFsaWduZWQuCgpgYGB7cn0Ka2FibGUoZGF0LCAiaHRtbCIpICAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAKCgpZb3UgY2FuIGFsc28gaGF2ZSB0aGUgdGFibGUgbW92ZSB0byB0aGUgbGVmdCBvciByaWdodCBzaWRlIG9mIHlvdXIgZG9jdW1lbnQgc28gdGhhdCB0ZXh0IG9yIGEgZmlndXJlIGNvdWxkIGJlIGluY2x1ZGVkIGJlc2lkZSBpdC4gSW4gdGhpcyBjYXNlLCBoYXZpbmcgdGhlIHRhYmxlIF9mbG9hdF8gcmlnaHQgYWxsb3dzIGZvciB0ZXh0IG9yIGltYWdlcyB0byBiZSBmb3JtYXR0ZWQgb24gdGhlIGxlZnQgc2lkZSBvZiB0aGUgcGFnZS4gRm9yIGV4YW1wbGUsIHdlIGNvdWxkIGNoYW5nZSB0aGUgZmlndXJlIHNpemUgYXMgd2VsbCBhbmQgaGF2ZSBhIGZpZ3VyZSBhbmQgdGFibGUgc2lkZS1ieS1zaWRlIGluIG91ciBkb2N1bWVudC4KCgpgYGB7ciAgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUV9CgprYWJsZShkYXQsICJodG1sIiwgZXNjYXBlID0gRikgICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiZmxvYXRfcmlnaHQiKSAKYGBgCgoKYGBge3IgZWNobyA9IEZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodCA9IDUsIG91dC53aWR0aD0nNTAlJywgb3V0LmhlaWdodD0nNTAlJ30KcGFyKG1hciA9IGMoNCw0LDEsMCkpCnBsb3QoY2FycyRzcGVlZCwgY2FycyRkaXN0KQpgYGAKCgpJbiB0aGlzIGNhc2UsIEkgc2hyYW5rIHRoZSBwbG90IHVzaW5nIGBvdXQud2lkdGhgIGFuZCBgb3V0LmhlaWdodGAgc28gdGhhdCBpdCB3b3VsZCBmaXQgYmVzaWRlIG91ciB0YWJsZS4KCmB7ciBwcmVzc3VyZSwgIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQgPSA1LCBvdXQud2lkdGg9JzUwJScsIG91dC5oZWlnaHQ9JzUwJSd9YAoKYGBge3IgZXZhbCA9IEZBTFNFfQpwYXIobWFyID0gYyg0LDQsMSwwKSkgI2FkanVzdGluZyBmaWd1cmUgbWFyZ2lucwpwbG90KGNhcnMkc3BlZWQsIGNhcnMkZGlzdCkKYGBgCgoKPC9icj4KCgoKCgo8L2JyPgoKIyMjIyNDdXN0b21pemUgd2l0aCBoaWdobGlnaHRpbmcgYW5kIGJvcmRlcnMuIAoKRm9yIHRoaXMgdGFibGUgYmxhY2sgbGluZXMgaGF2ZSBiZWVuIHNwZWNpZmllZCBhcyBjb2x1bW4gYm9yZGVycy4gQSByb3cgd2FzIHNwZWNpZmllZCB0byBiZSBoaWdobGlnaHRlZCBieSBhIHllbGxvdyBiYWNrZ3JvdW5kIGFzIHdlbGwgYXMgdG8gaGF2ZSB0aGUgdGV4dCBlbXBoYXNpemVkIGluIGJvbGQuIGBlc2NhcGUgPSBGQUxTRWAgZXNjYXBlcyBzcGVjaWNhbCBjaGFyYWN0ZXJzLiBJbiB0aGlzIGNhc2UgaXQgaW50ZXJmZXJlcyB3aXRoIG91ciBjb2x1bW4gdGl0bGVzLgoKYGBge3J9CmthYmxlKGRhdCwgImh0bWwiKSAlPiUKICAgICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQogICAgICAgY29sdW1uX3NwZWMoMToyLCBib3JkZXJfcmlnaHQgPSBUUlVFLCBib3JkZXJfbGVmdCA9IFRSVUUpICU+JQogICAgICAgcm93X3NwZWMoMywgYm9sZCA9IFQsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICJ5ZWxsb3ciKSAKYGBgCgojIyMjI0FkZCBmb290bm90ZXMuCgpGb290bm90ZXMgY2FuIGJlIGFkZGVkIHRvIGEgdGFibGUgdXNpbmcgc3ltYm9scyBvciBhbHBoYWJldCBtYXJrZXJzIGZvciBmbGFncy4gCgpUaGlzIGlzIGEgZ29vZCB0aW1lIHRvIGxlYXJuIG1vcmUgdXNlZnVsIGRhdGEgY2xlYW5pbmcgZnVuY3Rpb25zIGBwYXN0ZWAgYW5kIGBwYXN0ZTBgLiBUaGVzZSBhcmUgbWFkZSB0byBqb2luIG9yICdwYXN0ZScgc3RyaW5nIGNoYXJhY3RlcnMgdG9nZXRoZXIuIEluIHRoaXMgY2FzZSB3ZSB3YW50IHRvIHRha2UgYSBjaGFyYWN0ZXIgc3RyaW5nICh0aGUgdGl0bGUgb2YgZWFjaCBjb2x1bW4gb2Ygb3VyIGRhdGEgZnJhbWUpIGFuZCBhZGQgYSBmb290bm90ZSBzeW1ib2wgdG8gaXQgdG8gZGVub3RlIHVuaXRzLiBDYW4geW91IHRlbGwgd2hhdCB0aGUgZGlmZmVyZW5jZSBpcyBiZXR3ZWVuIHRoZSAyIGZ1bmN0aW9ucyBieSB0aGUgb3V0cHV0PwpgYGB7ciBtZXNzYWdlID0gRkFMU0V9CmNvbG5hbWVzKGRhdClbMV0gPC0gcGFzdGUoImNhcl8iLCBjb2xuYW1lcyhkYXQpWzFdLCBmb290bm90ZV9tYXJrZXJfc3ltYm9sKDEpKQpjb2xuYW1lcyhkYXQpWzJdIDwtIHBhc3RlMCgiY2FyXyIsIGNvbG5hbWVzKGRhdClbMl0sIGZvb3Rub3RlX21hcmtlcl9hbHBoYWJldCgxKSkKCmBgYApFc2NhcGUgaGFzIGJlZW4gY2hhbmdlZCB0byBGQUxTRSBzbyB0aGF0IHRoZSBodG1sIGVuY29kaW5nIG9mIG91ciBzdXBlcnNjcmlwdCBpcyBub3QgZXNjYXBlZC4gVGhlIGxlZ2VuZCBmb3IgdGhlIGZvb3Rub3RlIHN5bWJvbCBvciBjaGFyYWN0ZXIgYmVsb3cgdGhlIHRhYmxlIGlzIGFsc28gYWRkZWQgaW4gb3V0IGBrYWJsZWAgY2FsbC4KYGBge3J9CmthYmxlKGRhdCwgImh0bWwiLCBlc2NhcGUgPSBGQUxTRSkgJT4lCiAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUKICBjb2x1bW5fc3BlYygxOjIsIGJvcmRlcl9yaWdodCA9IFRSVUUsIGJvcmRlcl9sZWZ0ID0gVFJVRSkgJT4lCiAgcm93X3NwZWMoMywgYm9sZCA9IFQsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICJ5ZWxsb3ciKSAlPiUKICBmb290bm90ZShzeW1ib2wgPSAia2lsb21ldGVycyBwZXIgaG91ciIsIGFscGhhYmV0ID0gImtpbG9tZXRlcnM7IikgCmBgYAoKCiMjI0FkZGluZyBJbWFnZXMgdG8geW91ciBEb2N1bWVudAoKVG8gYWRkIHBpY3R1cmVzIHRvIHlvdXIgZG9jdW1lbnQ6CgogICAKICAgICEgWyNjYXB0aW9uIChvcHRpb25hbCldICAgKCNkaXJlY3RvcnkvZmlsZSkgICAgIHsjc2l6ZSAob3B0aW9uYWwpfSAgICAgCmAhW2tuaXRyIC0gZ2V0IGl0P10oaW1nL2tpdHRlbi13aXRoLXN0cmluZy5wamcpe3dpZHRoPTQwMHB4fWAgICAgIAoKCiFba25pdHIgLSBnZXQgaXQ/XShpbWcva2l0dGVuLXdpdGgtc3RyaW5nLmpwZyl7d2lkdGg9NDAwcHh9CgpNaW5pbXVtIHN5bnRheCB0byBhZGQgYW4gaW1hZ2UgKG5vIGNhcHRpb24sIGRlZmF1bHQgaW1hZ2Ugc2l6ZSk6ICAgICAKYCFbXShpbWcva2l0dGVuLXdpdGgtc3RyaW5nLmpwZylgCgojIyNUYWJsZSBvZiBjb250ZW50cwoKVGhpcyBpcyB0aGUgeWFtbCBoZWFkZXIgaW5jbHVkaW5nIHRoZSB0YWJsZSBvZiBjb250ZW50cyAodG9jKSBmb3IgdGhlIGxlc3Nzb24uIEl0IGlzIGFzIHNpbXBsZSBhcyB3cml0aW5nIGB0b2MgPSBUUlVFYCB1bmRlciB0aGUgb3V0cHV0IGZvciB0aGUgZG9jdW1lbnQgdHlwZSB5b3UgYXJlIHVzaW5nIGFuZCB0aGVuIHNwZWNpZnlpbmcgd2hhdCBsZXZlbCBvZiBoZWFkZXJzIChyZW1lbWJlciBvdXIgaGFzaHRhZ3MpIHlvdSB3b3VsZCBsaWtlIHRvIGluY2x1ZGUgaW4gdGhlIHRvYy4gSSBhbSBrZWVwaW5nIDFzdCwgMm5kLCBhbmQgM3JkIGxldmVsIGhlYWRlcnMgaW4gdGhpcyBleGFtcGxlLiBJZiBJIGhhZCBhIDR0aCBsZXZlbCBoZWFkZXIsIGl0IHdvdWxkIHN0aWxsIGJlIGxhcmdlciB0aGFuIG15IHRleHQsIGJ1dCBpdCB3aWxsIG5vdCBzaG93IHVwIGluIG15IHRhYmxlIG9mIGNvbnRlbnRzLiBUaGUgdG9jIGNyZWF0ZXMgYSBoeXBlcmxpbmsgdG8gZWFjaCBzZWN0aW9uIGZvciB0aGUgdXNlciB0byBuYXZpZ2F0ZSB0aGUgZG9jdW1lbnQuIGBDVFJMK1NISUZUK09gIG9wZW5zIHRoZSBkb2N1bWVudCBvdXRsaW5lIHdoaWNoIGFsbG93cyBuYXZpZ2F0aW9uIHRvIHRoZXNlIHNlY3Rpb25zIHdoaWxlIGNvZGluZy4gIAoKCiAgICAtLS0KICAgIHRpdGxlOiAiTGVzc29uIDQgLSBPZiBEYXRhIENsZWFuaW5nIGFuZCBEb2N1bWVudGF0aW9uIC0gQ29ucXVlciBSZWd1bGFyIEV4cHJlc3Npb25zLCBVc2UgUiBtYXJrZG93biBhbmQga25pdHIgdG8gbWFrZSBQREZzLCBhbmQgQ2hhbGxlbmdlIHlvdXJzZWxmIHdpdGggYSAnUmVhbCcgRGF0YXNldCIKICAgIG91dHB1dDogCiAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgICAga2VlcF9tZDogeWVzCiAgICAgICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICAgICAgdG9jX2RlcHRoOiAzCiAgICAgIGh0bWxfbm90ZWJvb2s6CiAgICAgICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICAgICAgdG9jX2RlcHRoOiAzCiAgICAtLS0KCgpZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGUgYmx1ZSBidXR0b24gdGhhdCBraW5kIG9mIGxvb2tzIGxpa2UgYW4gZXllYmFsbCBpbiB0aGUgdG9wIHJpZ2h0IGNvcm5lciBvZiB0aGUgVmlld2VyIFBhbmUgYXMgd2VsbCBhcyB0aGUgU291cmNlIFBhbmUgd2l0aCBhIGRyb3Bkb3duIHRoYXQgc2F5cyAnUHVibGlzaCcuIElmIHlvdSBhcmUgc3VwZXItcHJvdWQgb2YgeW91ciB3b3JrLCB5b3UgY2FuIHBvc3QgeW91ciByZW5kZXJlZCBkb2N1bWVudCBmb3IgZnJlZSwgZm9yIHRoZSB3b3JsZCB0byBzZWUgYXQgW1JwdWJzXShodHRwczovL3JwdWJzLmNvbS8pLiBJdCBjYW4gYmUgaW50ZXJlc3RpbmcgdG8gc2VlIHdoYXQgb3RoZXIgcGVvcGxlIGluIHRoZSBSIGNvbW11bml0eSBoYXZlIGJlZW4gd29ya2luZyBvbiBhcyB3ZWxsLgoKIyMjU2xpZGVzCgpTbGlkZXNob3dzIGNhbiBhbHNvIGJlIG1hZGUgZmFpcmx5IHNpbXBseSBpbiBSIG1hcmtkb3duLiBHbyB0byBgRmlsZSAtPiBOZXcgRmlsZSAtPiBSIFByZXNlbnRhdGlvbmAgYW5kIGNyZWF0ZSBhbiAuUlByZXMgZmlsZS4gU2xpZGVzIGFyZSBzZXBhcmF0ZWQgYnkgYSBzZXJpZXMgb2YgZXF1YWxzIGxpbmVzICg9PT0pIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHNsaWRlIGlzIGp1c3QgYWJvdmUgdGhlc2UgbGluZXMuCgoKICAgICAgRmlyc3QgU2xpZGUKICAgICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiAgICAgIEZvciBtb3JlIGRldGFpbHMgb24gYXV0aG9yaW5nIFIgcHJlc2VudGF0aW9ucyBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA0ODY0Njg+LgoKICAgICAgLSBCdWxsZXQgMQogICAgICAtIEJ1bGxldCAyCiAgICAgIC0gQnVsbGV0IDMKCiAgICBTbGlkZSBXaXRoIENvZGUKICAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgogICAgYGBge3J9CiAgICBzdW1tYXJ5KGNhcnMpCiAgICBgYGAKCiAgICBTbGlkZSBXaXRoIFBsb3QKICAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgogICAgYGBge3IgZXZhbCA9IEZBTFNFfQogICAgcGxvdChjYXJzKQogICAgYGBgCgoKSWYgeW91IGNsaWNrIG9uICdQcmV2aWV3JyBpbiB0aGUgU291cmNlIFBhbmUsIGEgUHJlc2VudGF0aW9uIFRhYiB3aWxsIG9wZW4gaW4gdGhlIEVudmlyb25tZW50IFBhbmUgd2l0aCBhIGEgc2xpZGVzaG93IHRoYXQgeW91IGNhbiB0b2dnbGUgdGhyb3VnaC4gSW4gdGhhdCBQYW5lIHVuZGVyICdNb3JlJyB5b3UgY2FuIGFsc28gJ1ZpZXcgaW4gQnJvd3Nlcicgb3IgJ1NhdmUgQXMgV2VicGFnZScsIHdoaWNoIGlzIHRoZSBjb21tb24gd2F5IHRoZXNlIHNsaWRlcyBnZXQgcHJlc2VudGVkLgoKSSByZWFsbHkganVzdCB3YW50ZWQgdG8gc2hvdyB5b3UgdGhhdCB0aGVzZSBzbGlkZXMgZXhpc3QuIERlcGVuZGluZyBvbiB3aGF0IHlvdSBhcmUgcHJlc2VudGluZywgdGhpcyBjb3VsZCBiZSBhIHF1aWNrIGFsdGVybmF0aXZlIHRvIFBvd2VycG9pbnQgaWYgeW91IGFyZSBuZWVkIHRvIHByZXNlbnQgc29tZSBjb2RlLiBBZ2FpbiwgdGhlc2UgYXJlIGN1c3RvbWl6YWJsZSA8aHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vaW9zbGlkZXNfcHJlc2VudGF0aW9uX2Zvcm1hdC5odG1sPi4KCklmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBhIHNlcGFyYXRlIHR1dG9yaWFsIG9uIG1ha2luZyBhbmQgY3VzdG9taXppbmcgaW9zbGlkZXMgb3IgdGhlIGZhbmNpZXIgW1NsaWRpZnldKGh0dHBzOi8vd3d3Lmp2Y2FzaWxsYXMuY29tL3NsaWRpZnlfdHV0b3JpYWwpIHNsaWRlcywgcGxlYXNlIGxlYXZlIGEgY29tbWVudCBpbiB0aGUgTGVzc29uIDQgc3VydmV5IChodHRwczovL3d3dy5zdXJ2ZXltb25rZXkuY29tL3IvUFZIREtEQikuCgoKKioqCgojI0EgUmVhbCBNZXNzeSBEYXRhc2V0CgpJIGxvb2tlZCBmb3IgYSBtZXNzeSBkYXRhc2V0IGZvciBkYXRhIGNsZWFuaW5nIGFuZCBmb3VuZCBpdCBpbiBhIGJsb2cgdGl0bGVkOiAgICAgClsiQmlvbG9naXN0czogdGhpcyBpcyB3aHkgYmlvaW5mb3JtYXRpY2lhbnMgaGF0ZSB5b3UuLi4iXShodHRwOi8vd3d3Lm9waW5pb21pY3Mub3JnL2Jpb2xvZ2lzdHMtdGhpcy1pcy13aHktYmlvaW5mb3JtYXRpY2lhbnMtaGF0ZS15b3UvKSAKICAgICAKVGhlIG1haW4gYW5kIGNvbW1vbiBpc3N1ZSB3aXRoIHRoaXMgZGF0YXNldCBpcyB0aGF0IHdoZW4gZGF0YSBlbnRyeSB3YXMgZG9uZSB0aGVyZSB3YXMgbm8gX3N0cnVjdHVyZWQgdm9jYWJ1bGFyeV87IHBlb3BsZSBjb3VsZCB0eXBlIHdoYXRldmVyIHRoZXkgd2FudGVkIGludG8gZnJlZSB0ZXh0IGFuc3dlciBib3hlcyBpbnN0ZWFkIG9mIHVzaW5nIGRyb3Bkb3duIG1lbnVzIHdpdGggbGltaXRlZCBvcHRpb25zLCBnaXZpbmcgYW4gZXJyb3IgaWYgc29tZXRoaW5nIGlzIGZvcm1hdHRlZCBpbmNvcnJlY3RseSwgb3Igc3RpcHVsYXRpbmcgc29tZSBydWxlcyAoaWUuIG11c3QgYmUgYWxsIGxvd2VyY2FzZSwgdXBwZXJjYXNlLCBubyBudW1iZXJzLCBzcGFjaW5nLCBldGMpLiAKCkkgbXVzdCBhZG1pdCBJIGhhdmUgYmVlbiBndWlsdHkgb2YgbWVzc2luZyB3aXRoIHBlb3BsZSB3aG8gaGF2ZSBtYWRlIGRhdGFiYXNlcyB3aXRob3V0IHJ1bGVzLiBGb3IgZXhhbXBsZSwgZ2l2aW5nIGFuIGVtZXJnZW5jeSBjb250YWN0LCB0aGVyZSB3YXMgYSBsaW5lIHRvIGlucHV0ICdSZWxhdGlvbnNoaXAnLCB3aGljaCBjb3VsZCBlYXNpbHkgaGF2ZSBiZWVuIGEgZHJvcGRvd24gbWVudTogJ3BhcmVudCwgcGFydG5lciwgZnJpZW5kLCBvdGhlcicuIEluc3RlYWQgSSB3YXMgYWxsb3dlZCB0byB3cml0ZSBpbiBhIGZyZWUgdGV4dCBib3ggJ2xpZmVsb25nIGtpbmRyZWQgc3Bpcml0LCBzb3VsbWF0ZSBhbmQgZG9nZ3ktZGFkZHknLiBJIGRvbid0IHRoaW5rIGFueW9uZSBoZXJlIHdhcyB0cnlpbmcgdG8gYmUgYSBudWlzYW5jZSwgdGhpcyBtZXNzeSBkYXRhIGlzIGp1c3QgYSBjb25zZXF1ZW5jZSBvZiBwb29yIGRhdGEgY29sbGVjdGlvbi4gCgogICAgCgoKX19DaGFsbGVuZ2U6X18gICAgICAKClRoaXMgaXMgW1dlbGxjb21lIFRydXN0IEFQQyBkYXRhc2V0XShodHRwczovL2dpdGh1Yi5jb20vZWFjdG9uL0NBR0VGL2Jsb2IvbWFzdGVyL0xlc3Nvbl80L2RhdGEvVW5pdmVyc2l0eSUyMHJldHVybnNfZm9yX2ZpZ3NoYXJlX0ZJTkFMLnhsc3gpIG9uIHRoZSBjb3N0cyBvZiBvcGVuIGFjY2VzcyBwdWJsaXNoaW5nIGJ5IHByb3ZpZGluZyBhcnRpY2xlIHByb2Nlc3NpbmcgY2hhcmdlIChBUEMpIGRhdGEuIAoKaHR0cHM6Ly9maWdzaGFyZS5jb20vYXJ0aWNsZXMvV2VsbGNvbWVfVHJ1c3RfQVBDX3NwZW5kXzIwMTJfMTNfZGF0YV9maWxlLzk2MzA1NAoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQ7bWFyZ2luOjAgMTBweCAxMHB4IDAiIG1hcmtkb3duPSIxIj4KIVtdKGltZy95b3Vnb3R0aGlzLmpwZyl7d2lkdGg9MjAwcHh9CjwvZGl2PgoKCldoYXQgSSB3YW50IHRvIGtub3cgaXM6IAoKICAxLiBMaXN0IDMgcHJvYmxlbXMgd2l0aCB0aGlzIGRhdGFzZXQgdGhhdCByZXF1aXJlIGRhdGEgY2xlYW5pbmcuCiAgMS4gV2hhdCBpcyB0aGUgbWVhbiBjb3N0IG9mIHB1Ymxpc2hpbmcgZm9yIHRoZSB0b3AgMyBtb3N0IHBvcHVsYXIgcHVibGlzaGVycz8gCiAgMS4gV2hhdCBpcyB0aGUgbnVtYmVyIG9mIHB1YmxpY2F0aW9ucyBieSBQTE9TIE9uZSBpbiBkYXRhc2V0PyAgICAgICAgICAgICAgICAgCiAgMS4gQ29udmVydCBzdGVybGluZyB0byBDQUQuIFdoYXQgaXMgdGhlIG1lZGlhbiBjb3N0IG9mIHB1Ymxpc2hpbmcgd2l0aCBFbHNldmllciBpbiBDQUQ/CiAgMS4gQW5ub3RhdGUgeW91ciBkYXRhIGNsZWFuaW5nIGVmZm9ydHMgYW5kIGFuc3dlcnMgdG8gdGhlc2UgcXVlc3Rpb25zIGluIGFuIC5SbWQgZmlsZS4gS25pdCB5b3VyIGZpbmFsIGFuc3dlcnMgdG8gcGRmLgoKVGhlIHJvdXRlIEkgc3VnZ2VzdCB0byB0YWtlIGluIGFuc3dlcmluZyB0aGVzZSBxdWVzdGlvbiBpczoKCiogSW5zcGVjdCB5b3VyIGRhdGFzZXQuIEFyZSB0aGUgZGF0YSB0eXBlcyB3aGF0IHlvdSBleHBlY3Q/CiogSWRlbnRpZnkgYW55IGltbWVkaWF0ZSBwcm9ibGVtcy4gKEFuc3dlciBRdWVzdGlvbiAjMSkKKiBDbGVhbiB1cCBjb2x1bW4gbmFtZXMuCiogRGF0YSBjbGVhbiB0aGUgcHVibGlzaGVyIGNvbHVtbi4KICAgIC0gY29udmVydCBhbGwgZW50cmllcyB0byBsb3dlcmNhc2UKICAgIC0gY29ycmVjdCB0eXBvcwogICAgLSBjb3JyZWN0IG11bHRpcGxlIG5hbWVzIGZvciBhIHB1Ymxpc2hlciB0byBvbmUgbmFtZQogICAgLSByZW1vdmUgbmV3bGluZSBjaGFyYWN0ZXJzIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlCiogQW5zd2VyIFF1ZXN0aW9ucyAjMi01CgoKClRoZXJlIGlzIGEgW1JFQURNRV0oaHR0cHM6Ly9naXRodWIuY29tL2VhY3Rvbi9DQUdFRi9ibG9iL21hc3Rlci9MZXNzb25fNC9kYXRhL1JlYWRtZV9maWxlLmRvY3gpIGZpbGUgdG8gZ28gd2l0aCB0aGlzIHNwcmVhZHNoZWV0IGlmIHlvdSBoYXZlIHF1ZXN0aW9ucyBhYm91dCB0aGUgZGF0YSBmaWVsZHMuICAKCjwvYnI+CgoKVGhlIGJsb2dnZXIncyBvcGluaW9uIG9mIGNsZWFuaW5nIHRoaXMgZGF0YXNldDoKCl8nSSBub3cgaGF2ZSBubyBoYWlyIGxlZnQ7IEnigJl2ZSB0b3JuIGl0IGFsbCBvdXQuICBNeSB0ZWV0aCBhcmUganVzdCBzdHVtcHMgZnJvbSBleGNlc3NpdmUgZ25hc2hpbmcuICBNeSBmYWl0aCBpbiBodW1hbml0eSBoYXMgYmVlbiBkZXN0cm95ZWQhJ18KCkRvbid0IGdldCB0byB0aGlzIHBvaW50LiBUaGUgZGF0YXNldCBkb2Vzbid0IG5lZWQgdG8gYmUgcGVyZmVjdC4gTm8gZGF0YXNldHMgYXJlIDEwMCUgY2xlYW4uIEp1c3QgZG8gd2hhdCB5b3UgZ290dGEgZG8gdG8gYW5zd2VyIHRoZXNlIHF1ZXN0aW9ucy4gIAoKV2UgY2FuIHRhbGsgYWJvdXQgaG93IHRoaXMgd2VudCBhdCB0aGUgYmVnaW5ubmluZyBvZiBuZXh0IHdlZWsncyBsZXNzb24uCgoqKioKCgoKICAgCl9fUmVzb3VyY2VzOl9fICAgICAKPGh0dHA6Ly9zdGF0NTQ1LmNvbS9ibG9jazAyMl9yZWd1bGFyLWV4cHJlc3Npb24uaHRtbD4gICAgIAo8aHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDI3X3JlZ3VsYXItZXhwcmVzc2lvbnMuaHRtbD4gICAgIAo8aHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDI4X2NoYXJhY3Rlci1kYXRhLmh0bWw+ICAgICAKPGh0dHA6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWw+CjxodHRwOi8vd3d3Lmdhc3RvbnNhbmNoZXouY29tL0hhbmRsaW5nX2FuZF9Qcm9jZXNzaW5nX1N0cmluZ3NfaW5fUi5wZGY+ICAgICAKPGh0dHA6Ly92YXJpYW5jZWV4cGxhaW5lZC5vcmcvci90cnVtcC10d2VldHMvPiAgICAgCjxodHRwOi8vd3d3Lm9waW5pb21pY3Mub3JnL2Jpb2xvZ2lzdHMtdGhpcy1pcy13aHktYmlvaW5mb3JtYXRpY2lhbnMtaGF0ZS15b3UvPiAgICAgCjxodHRwczovL2ZpZ3NoYXJlLmNvbS9hcnRpY2xlcy9XZWxsY29tZV9UcnVzdF9BUENfc3BlbmRfMjAxMl8xM19kYXRhX2ZpbGUvOTYzMDU0PiAgICAgCjxodHRwOi8vd3d3LmRhdGFjb21tdW5pdHlkYy5vcmcvYmxvZy8yMDEzLzA4L2ZhbnRhc3RpYy1wcmVzZW50YXRpb25zLWZyb20tci11c2luZy1zbGlkaWZ5LWFuZC1yY2hhcnRzLz4gICAgIAo8aHR0cHM6Ly9naXRodWIuY29tL3JkcGVuZy9jYWNoZXN3ZWF2ZS9ibG9iL21hc3Rlci9pbnN0L2RvYy9jYWNoZVN3ZWF2ZS5Sbnc+ICAgICAKPGh0dHA6Ly9lbWFpbHJlZ2V4LmNvbS8+ICAgICAKPGh0dHBzOi8vcmVnZXgxMDEuY29tLz4gICAgIAo8aHR0cHM6Ly9yZWdleHIuY29tLz4gICAgIAo8aHR0cHM6Ly93d3cucmVndWxhci1leHByZXNzaW9ucy5pbmZvL2JhY2tyZWYuaHRtbD4gICAgIAo8aHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDkvUmVnRXhDaGVhdHNoZWV0LnBkZj4gICAgIAo8aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RvZGF5LWlzLWEtZ29vZC1kYXkvRW1vdGljb25zL21hc3Rlci9lbURpY3QuY3N2PiAgICAgCjxodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tPiAgICAgCjxodHRwczovL3lpaHVpLm5hbWUva25pdHIvb3B0aW9ucy8jY2h1bmtfb3B0aW9ucz4gIAo8aHR0cHM6Ly93d3cuY3MuYmhhbS5hYy51ay9+YXhqL3B1Yi90ZWFjaGluZy8yMDE2LTcvc3RhdHMva25pdHItbWFudWFsLnBkZj4gICAgIAo8aHR0cHM6Ly95aWh1aS5uYW1lL2tuaXRyL2RlbW8vY2FjaGUvPiAgCjxodHRwczovL2hlbHAuZ2l0aHViLmNvbS9hcnRpY2xlcy9vcmdhbml6aW5nLWluZm9ybWF0aW9uLXdpdGgtdGFibGVzLz4gICAgICAKPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9rYWJsZUV4dHJhL3ZpZ25ldHRlcy9hd2Vzb21lX3RhYmxlX2luX2h0bWwuaHRtbCNnZXR0aW5nX3N0YXJ0ZWQ+ICAgICAKPGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2lvc2xpZGVzX3ByZXNlbnRhdGlvbl9mb3JtYXQuaHRtbD4gICAgIAo8aHR0cHM6Ly9ycHVicy5jb20vPiAgICAgIAo8aHR0cHM6Ly93d3cuanZjYXNpbGxhcy5jb20vc2xpZGlmeV90dXRvcmlhbD4gICAgIAo8aHR0cDovL3I0ZHMuaGFkLmNvLm56L3N0cmluZ3MuaHRtbD4KCgojUG9zdC1MZXNzb24gQXNzZXNzbWVudAoqKioKCllvdXIgZmVlZGJhY2sgaXMgZXNzZW50aWFsIHRvIGhlbHAgdGhlIG5leHQgY29ob3J0IG9mIHRyYWluZWVzLiBQbGVhc2UgdGFrZSBhIG1pbnV0ZSB0byBjb21wbGV0ZSB0aGUgZm9sbG93aW5nIHNob3J0IHN1cnZleToKaHR0cHM6Ly93d3cuc3VydmV5bW9ua2V5LmNvbS9yL1BWSERLREIKCjwvYnI+CgoqKioKCjwvYnI+CgpUaGFua3MgZm9yIGNvbWluZyEhIQoKIVtdKGltZy9yc3R1ZGlvLWJvbWIucG5nKXt3aWR0aD0zMDBweH0KCgo=